使用容器transcated和txsize

时间:2015-08-17 20:58:40

标签: spring-amqp

我有与帖子类似的要求: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。

1 个答案:

答案 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实施。