本文环境:
springboot 1.5.8.RELEASE + amqp-client 4.0.3.jar
记一个很神奇的bug。
场景:开启生产者确认模式,指定了自定义ConfirmCallback实现类。
尝试投递到不存在的exchange,成功回调confim接口,随后再次重发该信息。
但意外的是,不止没有回调confirm接口。还导致整个rabbitMQ卡死,发送消息到其他交换机一样无作用。
原因:
上述操作:“尝试投递到不存在的exchange”,会产生一个ERROR:
ERROR org.springframework.amqp.rabbit.connection.CachingConnectionFactory - Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'noexist' in vhost '/', class-id=60, method-id=40)
这个error,不管是否开启了生产者确认模式,都是会触发的。
但在实现了生产者确认模式后,会在confirm接口回调完毕之后再触发。
但因为在confirm回调中,又进行了重发消息的操作。
这样就形成了一个死循环:
- confirm不完成,error就不触发
- error不触发,RabbitMQ就不能提供服务
- RabbitMQ不提供服务,重发消息就无法完成,阻塞在那里
- 重发消息不完成,confirm就不能完成
禁止套娃
因此即使是发送到其他存在的交换机的信息,也会因为RabbitMQ无法提供服务,而阻塞。
但需要注意的是,其他任务是可以正常进行的(CPU没资源之类的除外)。辅助跑了个定时任务,是可以正常输出的。
解决办法:
很简单。就是不要在confirm里面重新发送,交给延迟队列或者定时任务完成,就解决了
直观的感受一下:
代码:
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("---------触发confirm-----------");
mqSender.reSendMiaoshaMessage(miaoshaMessage,
System.out.println("----------confirm触发完成----------");
}
});
public void reSendMiaoshaMessage(MiaoshaMessage mm,CorrelationData correlationData) {
System.out.println("重发消息");
String msg = RedisService.beanToString(mm);
rabbitTemplate.convertAndSend("notExist", MQConfig.MIAOSHA_ROUTING_KEY, msg, correlationData);
System.out.println("重发消息完成");
}
控制台输出:
这么神奇的bug,估计没几个人遇到,所以网上找过一轮都没有蛛丝马迹。
mark一下,能遇上这个bug的,感觉都是有缘人……
本文完,有误欢迎指出