3、 第三个问题原因分析
还是查看抓包信息
Basic.Reject
: 客户端发送Basic.Reject方法请求,表示无法处理消息,拒绝消息,此时的requeue参数为true,将消息返回原来的队列;
Basic.Deliver
: 服务端调用Basic.Deliver方法,和第一次Basic.Deliver方法不同的是,此时的redeliver参数为true,表示重新投递消息到监听队列的消费者,然后这两步会一直重复下去。
RabbitMQ消息监听程序异常时,consumer会向rabbitmq server发送Basic.Reject
,表示消息拒绝接受,由于Spring默认requeue-rejected
配置为true
,消息会重新入队,然后rabbitmq server重新投递。就相当于死循环了,所以容易导致消费端资源占用过高,特别是TCP连接数、线程数、IO飙升,如果个别程序带事务或数据库操作等连接资源得不到释放也会占满,导致应用假死状态(出现问题的时候,查看问题应用出现大量的connection timeout错误报错日志)。
因此针对性的,有些业务场景(不强调数据强一致性的场景,比如日志收集)可以设置default-requeue-rejected: false
即可。
factory.setDefaultRequeueRejected(false);
会根据异常类型选择直接丢弃或加入dead-letter-exchange中。
消费者端正确的使用手动确认示例结构代码,很重要!
try { // 业务逻辑。 }catch (Exception e){ // 输出错误日志。 }finally { // 消息签收。 }
4、 验证队列设置最大长度限制
设置queueLengthLimit队列最大长度限制 x-max-length=5
生产者原本想要生产10条消息
由于受到队列最大长度限制,实际上只有5条入队列里面。
消费者拿出来的消息,仅有5条,从NO.6~NO.10
改变消费者程序,让生产者一直产生消息,消费者消费速度明显赶不上生产者的生产速度。
从消费端来看消息是随机性入队的,队列里面一直最多5条消息,发再多也进不了,消息者和生产者也不会发生什么异常,只是消息会随机性丢失(并没有全部入队)。
运行情况良好,除了消息没有全部入队列 ,没有出现异常情况
消费比较慢,本机器CPU和内存各项指标正常,没有异常。
搞一个异常情况出现unack,最大队列长度限制,是不算unack数量的,如下图所示
异常之后,此观察MQ监控管理后台
生产者不停一直在生产消息,运行30分钟,观察生产者应用也是正常的的,就是消息入不了队列。
5、 检查实际的业务端代码
再看我们业务系统消费端代码,消费端各种不规范写法都有,以下例举几个典型
1、手动签收有ACK,但是没有try-catch-finally结构,消费端业务代码如下:
2、有try-catch-finally结构,但是deliverTag是一个固定值0,一样的会出问题。
3、自动签收确认的,大量消息的时候,容易搞死消费端应用。
6、 总结
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!