rabbitmq MessageConverter消息接收异常 一直unacked 解决
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
concurrency: 1 # Minimum number of consumers.
max-concurrency: 20 # Maximum number of consumers.
prefetch: 50
default-requeue-rejected: true #意思是,消息被拒后(即未消费),重新(true)放入队列
retry:
enabled: true #是否开启消费者重试(为false时关闭消费者重试,这时消费端代码异常会一直重复收到消息)
max-attempts: 3
initial-interval: 5000ms
这样,消费端发现了异常,尝试了规定次数后,这条“问题消息”就会被解决(如果设置了死信队列,就被送到了死信队列;否则直接扔掉)。是开启了“消费者重试尝试”的功劳。
如果不开启该模式,那么会无限的循环下去。和 “default-requeue-rejected: true”参数没有任何关系,
“消费者重试”模式会覆盖掉default-requeue-rejected(默认为true)。所以,只要是开了该模式,异常就可以被解决。
如果只设置 default-requeue-rejected: true(消费者重试未开启,应答方式为默认),那么会无限报错!
1.事故重现-磁盘占用飙升
一开始我不知道代码有问题,就是以为单纯的没有进行ack所以将ack模式改成auto自动,紧急升级了,这样不管正常与否,消息都会被签收,所以在当时确实是解决了问题。
其实现在回想起来是非常危险的操作的,将ack模式改成auto自动,这样会使QOS不生效。会出现大量消息涌入consumer从而造成consumer宕机,可以是因为当时在晚上,交易比较少,并且推送系统有多个节点,才没出现问题。
日志打印循环报错打印,导致服务器磁盘空间不够引起服务挂了。
2.原因
RabbitMQ消息监听程序异常时,consumer会向rabbitmq server发送Basic.Reject,表示消息拒绝接受,由于Spring默认requeue-rejected配置为true,消息会重新入队,然后rabbitmq server重新投递。就相当于死循环了,所以控制台在疯狂刷错误日志造成磁盘利用率飙升的原因。
3.复现方法:
在rabbitMq web客户端通过队列发送错误格式的数据,查看日志在频繁的刷新。
4.解决方法
将default-requeue-rejected: false即可。
5.总结
个人建议,生产环境不建议使用自动ack,这样会QOS无法生效。
在使用手动ack的时候,需要非常注意消息签收。
其实在将有问题的MQ重置时,是将错误的消息给清除才没有问题了,相当于是消息丢失了。
try {
// 业务逻辑。
}catch (Exception e){
// 输出错误日志。
}finally {
// 消息签收。
}
6.扩展:
试着把rabbitmq的配置改成手动确认消息:
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.direct.acknowledge-mode=manual
然后把确认消息的代码
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
放在finally{}里
7.如何终止错误格式的消息反复投递:
rabbitmq队列清理
在web管理端可以进行操作,选择到要操作的队列,下拉里面有一个Purge(不要选到delete), 点击之后,查看”Unasked“数量.