RabbitMQ删除队列不重启消费者,动态重启
项目框架使用的是springboot,关于springboot整合rabbitMQ博客一搜一大堆这里就不做赘述,此篇博客主要是记录一次生产bug
首先说明一下bug:
老大跑过来说,生产消息队列怎么没有收到别人推得消息,你看下
我:“好”,
我查看了日志,嗯确实没有收到,然后查看配置,mq地址也没有收到,这就很奇怪了,尝试本地起生产服务,然后重新发一条,能消费啊,我百思不得其解;对着配置看了好多遍,确定没有配错,这tm的就尴尬了
于是各种找原因尝试场景,最后发现是别同时把队列删除了,这就很尴尬了,看生产报错日志有这么一段
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[topic.woman] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:733) [spring-rabbit-2.3.6.jar:2.3.6] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.passiveDeclarations(BlockingQueueConsumer.java:608) [spring-rabbit-2.3.6.jar:2.3.6] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:595) [spring-rabbit-2.3.6.jar:2.3.6] at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.initialize(SimpleMessageListenerContainer.java:1347) [spring-rabbit-2.3.6.jar:2.3.6] at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1192) [spring-rabbit-2.3.6.jar:2.3.6] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_291] Caused by: java.io.IOException: null at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:1012) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:46) ~[amqp-client-5.10.0.jar:5.10.0] at org.springframework.amqp.rabbit.connection.PublisherCallbackChannelImpl.queueDeclarePassive(PublisherCallbackChannelImpl.java:358) ~[spring-rabbit-2.3.6.jar:2.3.6] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_291] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_291] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_291] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_291] at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:1157) ~[spring-rabbit-2.3.6.jar:2.3.6] at com.sun.proxy.$Proxy133.queueDeclarePassive(Unknown Source) ~[na:na] at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:711) [spring-rabbit-2.3.6.jar:2.3.6] ... 5 common frames omitted Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'topic.woman' in vhost '/', class-id=50, method-id=10) at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:502) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:293) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:141) ~[amqp-client-5.10.0.jar:5.10.0] ... 15 common frames omitted Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'topic.woman' in vhost '/', class-id=50, method-id=10) at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:517) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:341) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:739) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47) ~[amqp-client-5.10.0.jar:5.10.0] at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:666) ~[amqp-client-5.10.0.jar:5.10.0] ... 1 common frames omitted 2022-01-16 16:58:10.977 WARN 4892 --- [ntContainer#0-2] o.s.a.r.listener.BlockingQueueConsumer : Failed to declare queue: topic.woman 2022-01-16 16:58:10.977 WARN 4892 --- [ntContainer#0-2] o.s.a.r.listener.BlockingQueueConsumer : Queue declaration failed; retries left=2
最终2022-01-16 16:58:21.055 ERROR 4892 --- [ntContainer#0-2] o.s.a.r.l.SimpleMessageListenerContainer : Stopping container from aborted consumer 有这句话是消费者已经停止了
原因是因为这个BlockingQueueConsumer 配置了当找不到队列时只尝试3次,且间隔是5秒也就是意味着15秒没有找到队列就会宕机每次执行也会提示剩余尝试次数这个次数配置更改不了,
尽管手动添加或者是生产者重启,消费者没有重启消费者是收不到消息的后台队列也是没有消费者在线的
要想重新监听,只能重启服务,但是如果以后队列又被删除难道每次重启生产服务?显然这不是最好的解决方案,想解决就是想办法让其一一直重试或者监听不到就重启服务
写个配置类
package com.blackfish.rabbitMQ.config; import java.util.Arrays; import org.springframework.amqp.rabbit.listener.BlockingQueueConsumer; import org.springframework.amqp.rabbit.listener.ListenerContainerConsumerFailedEvent; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import lombok.extern.slf4j.Slf4j; /** * MQ消费者失败事件监听器 */ @Slf4j @Component public class ListenerContainerConsumerFailedEventListener implements ApplicationListener<ListenerContainerConsumerFailedEvent> { @Override public void onApplicationEvent(ListenerContainerConsumerFailedEvent event) { log.error("消费者失败事件发生:{}", event); // BlockingQueueConsumer if (event.isFatal()) { log.error(String.format("Stopping container from aborted consumer. Reason::%s.", event.getReason()), event.getThrowable()); SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) event.getSource(); String queueNames = Arrays.toString(container.getQueueNames()); // 重启 try { restart(container); log.info("重启队列%s的监听成功!", queueNames); } catch (Exception e) { log.error(String.format("重启队列%s的监听失败!", queueNames), e); } } } /** * 重启监听 * @param container * @return */ private void restart(SimpleMessageListenerContainer container) { // 暂停30s try { Thread.sleep(30000); } catch (Exception e) { log.error(e.getMessage()); } Assert.state(!container.isRunning(), String.format("监听容器%s正在运行!", container)); container.start(); } }
加上这个配置类就不需要手动重启了,感觉写个配置类有点笨重,如果能通配置可以更改重试次数或者间隔事件,这是最好的,等细细研究是否能简便些,如果有更好的请不要吝啬哦
努力提高自己的技术,不忘初心