我有与帖子类似的要求:link
(我在那里添加了评论并得到了回复,但发布了作为发布代码的新问题)
基本上我需要在队列侦听器中组合10个消息,然后调用传递组合消息的第三方服务(消息体是json,所以基本上我需要将10个json字符串连接成一个)。这是传统的非常规使用,但如果我能做到这一点,这是我流程中最好的地方。
我有以下代码,但它仍然无效。我已在队列中发布了3条消息,并且所有三条消息都是单独执行的。显然我的理解是错误的。我认为是因为侦听器只收到3条消息(小于txsize 10),所以在收到10条消息之前它不会收到任何消息。
@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
..
..
container.setChannelTransacted(true);
container.setPrefetchCount(10);
container.setTxSize(10);
return container;
}
private int txcount = 1;
List<String> syncList = Collections.synchronizedList(new ArrayList<String>());
@Bean
public MessageListener messageListener() {
return new MessageListener() {
public void onMessage(Message message) {
ObjectMapper mapper = ConfiguredObjectMapper.getObjectMapper();
String json = new String(message.getBody(), StandardCharsets.UTF_8);
syncList.add(json);
txcount++;
System.out.println("###############txcount is: " + txcount);
if (txcount == 1000){ //call remote serivice - join list and pass as parameter}
}
}
}
#
编辑1 我从其他帖子尝试了amqp客户端消费者,似乎工作正常。但我仍然想尝试使用txSize的监听器并比较性能和放大器。失败情景。
public static void main(String[] args) throws IOException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setUsername("rabbituser");
connectionFactory.setPassword("rabbitpwd");
connectionFactory.setVirtualHost("svi_vhost");
boolean autoReconnect = true;
int reconnectInterval = 5000;
int prefetch = 10;
List<String> messages = null;
Long lastDeliveryTag = null;
while (true) {
Connection connection = null;
try {
connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicQos(prefetch);
QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false;
channel.basicConsume("my.queue", false, consumer);
while (true) {
try {
QueueingConsumer.Delivery delivery = consumer.nextDelivery(500);
if (delivery == null) {
if (messages != null && messages.size() > 0) {
callRemoteService(String.join("\n", messages));
messages = null;
channel.basicAck(lastDeliveryTag, true);
}
}else {
if (messages == null) messages = new ArrayList<String>(prefetch);
String json = new String(delivery.getBody(), StandardCharsets.UTF_8);
lastDeliveryTag = delivery.getEnvelope().getDeliveryTag();
messages.add(json);
if (messages.size() == prefetch) {
callRemoteService(String.join("\n", messages));
messages = null;
channel.basicAck(lastDeliveryTag, true);
}
}
}catch(Exception ex){
LOGGER.info(ex.getMessage(), ex);
throw new AmqpRejectAndDontRequeueException("Error Processing message:"+messages.toString());
}
}
}
catch (Exception e) {
LOGGER.warn(e.getMessage());
}
if (autoReconnect) {
try {
releaseConn(connection);
if (connection != null) connection.close();
LOGGER.info("[*] Will try to reconnect to remote host in " + reconnectInterval / 1000 + " seconds.");
Thread.sleep(reconnectInterval);
}catch (InterruptedException ie) {
}
}
else
break;
}
}
#
编辑2
@Artem,我正在尝试编写一个应该收集N个消息的侦听器(如果q有N个或更多消息),然后连接消息体以形成json字符串,然后调用远程微服务将连接的字符串作为参数传递。远程服务可以从1到N个消息中加入json,但是如果我们从N(10+)个消息发送json,性能会好得多。所以基本上我想预取N个消息,然后循环遍历每个消息,将消息体附加到json字符串然后调用远程服务。我已经用basicConsume(上面列为UPDATE 1)编写了一个ampq客户端使用者,它看起来工作正常,但我想看看是否有办法使用一个可用的spring监听器来获得更好的异常/连接错误/重启方案。在另一篇文章中提到使用transactions / txSize,我可以遍历消息,并且在txSize循环之后将发送auto ack。
答案 0 :(得分:0)
是的,看起来你有点误解了它。
让我与代码分享它的外观:
private boolean doReceiveAndExecute(BlockingQueueConsumer consumer) throws Throwable {
Channel channel = consumer.getChannel();
for (int i = 0; i < txSize; i++) {
logger.trace("Waiting for message from consumer.");
Message message = consumer.nextMessage(receiveTimeout);
if (message == null) {
break;
}
try {
executeListener(channel, message);
}
catch (ImmediateAcknowledgeAmqpException e) {
break;
}
catch (Throwable ex) {//NOSONAR
consumer.rollbackOnExceptionIfNecessary(ex);
throw ex;
}
}
return consumer.commitIfNecessary(isChannelLocallyTransacted(channel));
}
正如您所看到的那样,框架在txSize
处有一个循环,并且在循环结束时,它为已获取的消息执行ack
。
如果消费者没有返回消息,则会有一个break
,最终会得到ack
完全针对在此周期内可以获取的消息数。
在此之后ListenerContainer
以相同的逻辑开始新的迭代。
所以,你的txcount++;
逻辑确实是错误的。
当Gary回答你时,没有这样的内置功能。
对我而言,您应该查看Spring Integration及其Aggregator
实施。