简体   繁体   English

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

[英]How can i stop my SimpleMessageListenerContainer from getting stuck in shutdown/restart loop?

I have a SimpleMessageListenerContainer which I am using with RabbitMQ and Java. 我有一个用于RabbitMQ和Java的SimpleMessageListenerContainer。 In most instances I am having no problems, however on some occassions when a message is sent to a queue there appears to be an exception which causes the SMLC to get in a loop trying to shutdown and then restart the queue. 在大多数情况下,我没有问题,但是在某些情况下,将消息发送到队列时,似乎会出现异常,这会导致SMLC陷入尝试关闭然后重新启动队列的循环。

[10/03/15 17:09:38:161 UTC] 00000246 SimpleMessage W org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer run Consumer raised exception, processing can restart if the connection factory supports it. [10/03/15 17:09:38:161 UTC] 00000246 SimpleMessage W org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer run消费者引发的异常,如果连接工厂支持,则处理可以重新启动。 Exception summary: org.springframework.amqp.AmqpIOException: java.io.IOException 异常摘要: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 local queue size=0 [10/03/15 17:09:38:189 UTC] 00000246 SimpleMessage I org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer run重新启动使用者:tag = [null],channel =缓存的兔子频道:AMQChannel(amqp ://epa_devint1@xx.xx.xx.xx:5782 /,1),acceptMode = AUTO本地队列大小= 0

[10/03/15 17:09:39:164 UTC] 00000256 SimpleMessage W org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer run Consumer raised exception, processing can restart if the connection factory supports it. [10/03/15 17:09:39:164 UTC] 00000256 SimpleMessage W org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer $ AsyncMessageProcessingConsumer run消费者引发的异常,如果连接工厂支持,则处理可以重新启动。 Exception summary: com.rabbitmq.client.ShutdownSignalException: connection error; 异常摘要:com.rabbitmq.client.ShutdownSignalException:连接错误; reason: {#method(reply-code=541, reply-text=INTERNAL_ERROR, class-id=0, method-id=0), null, ""} 原因:{#method(reply-code = 541,Reply-text = INTERNAL_ERROR,class-id = 0,method-id = 0),null,“”}

When i remove the message from the queue through the admin interface then there are no more exceptions. 当我通过管理界面从队列中删除消息时,没有更多例外了。

I believe the cause of the exception is something like a missing header property. 我相信导致异常的原因是缺少标头属性。

What is the proper way to handle this exception such that the message is removed from the queue and the shutdown/restart logic is exited? 什么是处理此异常的正确方法,以便从队列中删除消息并退出关闭/重新启动逻辑?

   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.Release spring-framework: 3.2.1.Release JDK:1.6弹簧-兔子:1.1.3。释放弹簧框架:3.2.1

Your problem is caused by the transactional nature of your message handling. 您的问题是由消息处理的事务性引起的。 I mean this: 我的意思是:

container.setTransactionManager(transactionManager);

What is happening is, that in some cases (probably some issue with message header, as you suggested) you encounter some error, and that is not being handled properly. 发生的情况是,在某些情况下(可能是您建议的消息标题出现问题),您会遇到一些错误,并且无法正确处理。 The error propagates through and reaches the txManager, which in turn: 该错误通过txManager传播并到达txManager,后者依次为:

  • does not acknowledge the message, and returns the message to the queue 不确认消息,并将消息返回到队列
  • considers your current connection corrupted , and closes it, so it can open a brand new one 认为您当前的连接已损坏 ,并关闭它,因此它可以打开一个全新的连接

Now this behavior above absolutely makes sense when you had an issue which is not a direct consequence of your message. 现在,当您遇到的问题并非消息直接导致的时候,上述行为绝对有意义。 Let's say you encountered a timeout while processing the message; 假设您在处理邮件时遇到超时; the application was shutting down while processing it; 该应用程序正在处理时正在关闭; in all these cases redelivering the message later OR to another node does make sense. 在所有这些情况下,稍后再传递消息或将消息重新传递到另一个节点确实有意义。

In your case however does not matter when and does not matter which node receives the faulty message, you will get the same problem, and you need manually delete that message. 但是,对于您而言,无论何时接收哪个节点都无所谓,也有问题,您将遇到相同的问题,并且需要手动删除该消息。 In order to avoid this situation you can: 为了避免这种情况,您可以:

  • programmatically filter out invalid messages and do not return them to the queue either by 以编程方式过滤掉无效消息,并且不要通过以下方式将它们返回到队列中

    • doing checks before processing it (prevent the fatal error happening) 在处理之前进行检查(防止发生致命错误)
    • handling the fatal errors like this in a different way (proper error handling) 以不同的方式处理致命错误(适当的错误处理)
  • use RabbitMQ features to deal with errors depending on your requirements 根据您的需求使用RabbitMQ功能处理错误

    • Dead Letter Queue 死信队列
    • Broker side validation 经纪人方面的验证
    • Probably TTL would work too TTL可能也可以

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 重新启动RabbitMQ的主节点时,SimpleMessageListenerContainer停止从RabbitMQ使用 - SimpleMessageListenerContainer stop to consume from RabbitMQ when restart the primary node of RabbitMQ 如何重新启动SimpleMessageListenerContainer - How to restart a SimpleMessageListenerContainer 如何在SimpleMessageListenerContainer中停止侦听器 - How to stop listeners in SimpleMessageListenerContainer 如何避免出现意外错误时关闭SimpleMessageListenerContainer? - How to avoid shutdown of SimpleMessageListenerContainer in case of unexpected errors? 如何停止Java循环吃掉> 50%的CPU? - How can I stop a Java while loop from eating >50% of my CPU? 如何阻止我的Android应用程序获取java.net.URL的MOBILE(REDIRECTED)版本? - How can I stop my Android app from getting the MOBILE (REDIRECTED) version of a java.net.URL? SimpleMessageListenerContainer - 消费者引发异常,如果连接工厂支持,则处理可以重新启动 - SimpleMessageListenerContainer - Consumer raised exception, processing can restart if the connection factory supports it 如何使用Java重新启动Applet? - How can I restart my Applet in Java? 如何在Ubuntu中启动,停止和重新启动bash脚本 - How can I make a bash script start, stop and restart in ubuntu 如何停止并重新启动Android Location Service - How can I stop and restart Android Location Service
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM