【夯实RabbitMQ】消费端消息确认与重试机制

目录

一、消费端消息确认机制

二、消费端消息重试机制


一、消费端消息确认机制

        在SpringBoot中,消费端可以配置消息确认模式。共有3种。

#acknowledge-mode.NONE:发送到消费端后就自动确认,消息被删除
#acknowledge-mode.MANUAL:手动确认,必须要收到ack或者nack
#acknowledge-mode.AUTO:程序在执行中,消息会保存,直到程序正常执行完或者抛出异常才删除消息
spring.rabbitmq.listener.simple.acknowledge-mode=AUTO
  • MANUAL:手动确认,必须要收到ack或者nack
  • AUTO:程序在执行中,消息会保存,直到程序正常执行完或者抛出异常才删除消息
  • NONE:发送到消费端后就自动确认,消息被删除

(1)MANUAL手动确认模式

        程序中需要指明消息是否被成功消费。如下程序,需要编写程序显示的告诉消息是否消费。

        成功消费返回ack。

        未成功消费返回nack。

        reject表示拒绝(如果不设置重回队列,效果相当于ack。消费端提示这条数据在消费端已经成功消费,但是这是垃圾数据,MQ大哥你可以把这条消息删了)。

 

(2)AUTO自动确认模式

        其实这里的自动消费理解很简单。也就是说消费端的代码有没有执行完。无非是两种情况。第一种程序正常执行完毕,消息被正常消费并会被删除。第二种,程序执行过程中抛出异常,消息被正常消费并会被删除。目前,我只找到了一种情况,可以让消息保留在内存中。且看如下代码。

        我在程序执行到等待20秒的中途,将消费端服务停掉,模拟程序执行到一半消费端服务器宕机。因为此在消费端消息没有真正得执行完毕(即没有正常执行完毕,也没有抛出任何异常),所以消息不会被RabbitMQ删除。此消息会一直保留在服务器中。并且MQ管理端的Unacked从1变成0。Ready从0变成1说明下次消费端在启动的时候会立即消费它。

(3)NONE不确认模式

        这个很好理解,也就是说消息只要被消费端接收,就自动从MQ队列中删除,RabbitMQ不关心消费端执行的情况。正所谓事不关己高高挂起……

二、消费端消息重试机制

        消息在消费端有可能在执行过程中出现异常。RabbitMQ提供了消息重试机制。如果消息出现异常,那么会把消息重新退回队列,然后再次消费该消息。但是重试的次数是有限的。

        下面是关于重试机制的相关配置。

#开启重试机制
spring.rabbitmq.listener.simple.retry.enabled=true
#最大重试次数
spring.rabbitmq.listener.simple.retry.max-attempts=3
#重试间隔时间
spring.rabbitmq.listener.simple.retry.max-interval=1000

        在消费端,如果程序连续出现报错N次后,会触发MessageRecoverer接口。接口源码如下,上面注释说如果所有重试次数用完后,会执行recover方法。

        因此,我们需要为系统配置MessageRecoverer接口实现。RabbitMQ提供了两种不同的实现。

        RejectAndDontRequeueRecoverer:仅仅打印一下错误日志

        RepublishMessageRecoverer:重新发布消息到指定路由器,并携带路由键;

        下面是目前我测试使用的代码,我用的是上述重新发布的MessageRecoverer接口实现。它表示如果重试次数达到最大限制,那么将消息发送到directExchange交换机,此消息的路由键是foodRoutingKey。

        使用Retry机制还有如下注意点(均为亲自测试结论):

        retry机制会在程序直接抛出异常后N次后调用MessageRecoverer接口实现类。被try-catch语句捕获的异常如果没有再次抛出,MQ会认为程序正常执行完。不会触发Retry机制。因此,使用了Retry机制,请直接抛出异常,不要用try-catch。如果要用,也要在try-catch中再次抛出异常,来告诉MQ这段程序出错了,MQ会帮我们把消息重新退出队列。如果使用了try-catch且未再次抛出异常,那么就直接被认为程序正常执行了,这样Retry机制不生效。      

         第二点,如果使用nack将消息重新推回队列,这条消息被视为一条新的消息,MQ认为新消息的消费失败次数是0,这样可能就会导致死循环,消费失败再退回再消费失败,因此表现为重试机制不生效。

         最后,如果需要使用retry机制,我个人建议消费端响应模式使用AUTO。原因很简单,如果使用手动模式MANUAL,程序在执行途中就出现异常,就不会执行到最后一行手动确认消息的代码,消费端就没有机会用ack去响应MQ。因此 ,在MQ管理端表现为会一直存在一条消息没有被消费。

        阅读更多:从头开始学RabbimtMQ目录贴

posted @ 2022-07-17 12:13  小大宇  阅读(227)  评论(0编辑  收藏  举报