如何阻止SimpleMessageListenerContainer陷入关机/重启循环?

时间:2015-03-10 19:58:31

标签: java spring rabbitmq amqp

我有一个SimpleMessageListenerContainer,我正在使用RabbitMQ和Java。在大多数情况下,我没有遇到任何问题,但是在某些情况下,当消息被发送到队列时,似乎有一个异常导致SMLC进入循环尝试关闭然后重新启动队列。

  

[10/03/15 17:09:38:161 UTC] 00000246 SimpleMessage W   org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer   运行Consumer引发异常,如果处理可以重启   连接工厂支持它。例外摘要:   org.springframework.amqp.AmqpIOException:java.io.IOException

     

[10/03/15 17:09:38:189 UTC] 00000246 SimpleMessage I   org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer   run Restarting Consumer:tag = [null],channel = Cached Rabbit Channel:   AMQChannel(AMQP://epa_devint1@xx.xx.xx.xx:5782 /,1),   acknowledgeMode = AUTO本地队列大小= 0

     

[10/03/15 17:09:39:164 UTC] 00000256 SimpleMessage W   org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer   运行Consumer引发异常,如果处理可以重启   连接工厂支持它。例外摘要:   com.rabbitmq.client.ShutdownSignalException:连接错误;原因:   {#method(reply-code = 541,reply-text = INTERNAL_ERROR,   class-id = 0,method-id = 0),null,“”}

当我通过管理界面从队列中删除邮件时,没有更多例外。

我认为异常的原因类似于缺少标题属性。

处理此异常的正确方法是什么,以便从队列中删除消息并退出关闭/重启逻辑?

   public SimpleMessageListenerContainer createMessageListenerContainer(Object consumer, String exchangeName, String queueName, String routingKey) {
    TopicExchange exchange = new TopicExchange(exchangeName);
    Queue queue = new Queue(queueName,
            MessagingConstants.RABBIT_MQ_QUEUE_DURABLE,
            MessagingConstants.RABBIT_MQ_QUEUE_EXCLUSIVE,
            MessagingConstants.RABBIT_MQ_QUEUE_AUTO_DELETE,
            MessagingConstants.RABBIT_MQ_QUEUE_ARGUMENTS);

    amqpAdmin.declareExchange(exchange);
    amqpAdmin.declareQueue(queue);
    amqpAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with(routingKey));

    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
    container.setQueues(queue);
    container.setConcurrentConsumers(MessagingConstants.RABBIT_MQ_CONCURRENT_CONSUMERS);
    container.setErrorHandler(errorHandler);
    container.setMessageListener(new MessageListenerAdapter(consumer, null));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setAdviceChain(retryAdviceChainFactory.createRequestRequeueExceptionAwareRetryChain(MessagingConstants.RABBIT_MQ_RETRY_ATTEMPTS));
    container.setChannelTransacted(true);
    container.setTaskExecutor(taskExecutor);
    container.setTransactionManager(transactionManager);
    return container;
}

@Override
public void afterPropertiesSet() {
    com.rabbitmq.client.ConnectionFactory rabbitFactory = new com.rabbitmq.client.ConnectionFactory() {
        protected void configureSocket(Socket socket) throws IOException {
            super.configureSocket(socket);
            socket.setSoTimeout(propertiesHolder.getRabbitMQSocketTimeoutMS());
        }
    };
    rabbitFactory.setConnectionTimeout(propertiesHolder.getRabbitMQConnectionTimeoutMS());
    rabbitFactory.setRequestedHeartbeat(propertiesHolder.getRabbitMQRequestedHeartbeatInSeconds());

    CachingConnectionFactory cachingFactory = new CachingConnectionFactory(rabbitFactory);
    cachingFactory.setAddresses(propertiesHolder.getRabbitMQHost());
    cachingFactory.setPort(propertiesHolder.getRabbitMQPort());
    cachingFactory.setUsername(propertiesHolder.getRabbitMQUsername());
    cachingFactory.setPassword(propertiesHolder.getRabbitMQPassword());
    cachingFactory.setChannelCacheSize(propertiesHolder.getRabbitMQChannelCacheSize());

    connectionFactory = cachingFactory;
    retryAdviceChainFactory = new RetryAdviceChainFactory();
    amqpAdmin = new RabbitAdmin(connectionFactory);

    errorHandler = new ErrorHandler() {
        @Override
        public void handleError(Throwable e) {
            LOG.error("Error occurred", e);
        }
    };

    TopicExchange deadLetterExchange = new TopicExchange(MessagingConstants.RABBIT_MQ_DEAD_LETTER_EXCHANGE);
    Queue deadLetterQueue = new Queue(
            MessagingConstants.RABBIT_MQ_DEAD_LETTER_QUEUE,
            MessagingConstants.RABBIT_MQ_QUEUE_DURABLE,
            MessagingConstants.RABBIT_MQ_QUEUE_EXCLUSIVE,
            MessagingConstants.RABBIT_MQ_QUEUE_AUTO_DELETE,
            MessagingConstants.RABBIT_MQ_QUEUE_ARGUMENTS);
    amqpAdmin.declareExchange(deadLetterExchange);
    amqpAdmin.declareQueue(deadLetterQueue);
    amqpAdmin.declareBinding(BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange).with("#"));
    messageRecoverer = new DeadLetterMessageRecoverer(rabbitTemplate(), deadLetterExchange);
}
JDK:1.6 spring-rabbit:1.1.3。发布 spring-framework:3.2.1。发布

1 个答案:

答案 0 :(得分:1)

您的问题是由邮件处理的事务性质引起的。我的意思是:

container.setTransactionManager(transactionManager);

正在发生的事情是,在某些情况下(可能是邮件标题的某些问题,正如您所建议的那样)您遇到了一些错误,而且没有正确处理。错误传播并到达txManager,而txManager依次为:

  • 不确认消息,并将消息返回队列
  • 认为您当前的连接已损坏,并关闭它,因此可以打开一个全新的连接

现在,当您遇到的问题不是您的消息的直接后果时,上述行为绝对有意义。我们假设您在处理邮件时遇到超时问题;应用程序在处理时关闭;在所有这些情况下,稍后重新传递消息或重新传递给另一个节点确实有意义。

在您的情况下,无论何时无关紧要哪个节点收到错误消息,您将遇到同样的问题,您需要手动删除该消息。为了避免这种情况,你可以:

  • 以编程方式过滤掉无效消息,并且不会通过

    将它们返回到队列中
    • 在处理之前进行检查(防止发生致命错误)
    • 以不同的方式处理这样的致命错误(正确的错误处理)
  • 使用RabbitMQ功能根据您的要求处理错误

    • 死信队列
    • 经纪人方验证
    • 可能TTL也会起作用