2.14. 自动和手动ack(补充篇)

未匹配的标注

卧槽!终于下班啦!有个毛病,下班不写篇博客就跟有毛病似的!
这篇博客主要讲自动ack和手动ack 以及 我知道的三种消费者消费消息的时候如何避免消息丢失的手段!
昨天有博友私信我,不理解rabbitmq代码当中的simple模式下消费者代码里面的接收消息当中的aotuAck是啥意思?
如下图就是这:
rabbitmq自动ack和手动ack(补充篇)
其实代码注释里面已经写得很清楚了!也许博友不明白的是为啥要这样做?
对自动ack和手动ack先来一波脑补吧(基础很重要):
当消息一旦被消费者接收,队列中的消息就会被删除。那么问题来了:RabbitMQ怎么知道消息被接收了呢?
这就要通过消息确认机制(Acknowlege)来实现了。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:

  • 自动ACK:消息一旦被接收,消费者自动发送ACK
  • 手动ACK:消息接收后,不会发送ACK,需要手动调用
    这两ACK要怎么选择呢?这需要看消息的重要性:
  • 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
  • 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
    实际上自动应答是存在缺陷的!为什么呢?比如消费端消费了队列里面的消息,读取出来了然后执行正常业务逻辑插入数据库,这个时候服务端就自动ack删除掉了队列里面的这个消息了,但是呢,你sql执行失败了,也就是正常的业务逻辑失败了,那么你回过头来一看,卧槽它妈妈,队列里面的消息也给我删除啦!那岂不是造成了业务失败了并且队列里面的消息也丢失的情况?是吧!是!
    如果你是使用的手动ack,等到正常的业务逻辑也就是入库成功了你再告诉服务器可以删除队列里面的消息啦,服务器才会去删除也就不存在上述的情况啦!这么看来手动ack还是比自动ack要安全一些的!但是手动ack也是存在一些弊端的比如性能,更深入的就不谈了,因为我也没太深入的了解,因为没他么必要这么深入的玩!我能解决问题就ok啦!

我们再把话题扯得远一些,很多人喜欢用redis里面的list类型模拟队列来解决高并发,那完全是扯犊子的做法,当然应付小的流量还是可以的,如果使用list模拟队列那么就会存在上述所说的自动ack存在的问题:消息丢失 所以在使用list模拟队列的时候小朋友不要忘记做一套消息丢失的补偿措施,看好了是补偿 不是补救 因为你救不了!如何补偿呢?请看我redis实战博文里面 有过详细描述:redis实战总结二(队列篇)
除了我的redis实战总结(队列篇)里面提到的,在redis5新版当中出现了stream类型,这个stream可牛逼的不行!号称乞丐版的kafka,可以完美的避开消息丢失情况的发生,应对大点的流量是没什么问题的,但是如果超大流量上千万pv了都那还得是mq更加适合!
不再废话了,上边说了list模拟队列以及stream新类型如何避免消费者消费时候消息丢失的手段,当然还有mq当中的手动ack机制也可以完美避免消息丢失!
写到这吧,下班回家被孩子玩去了!

以下是百度到的自行脑补吧:
1):什么是消息确认ACK。
  答:如果在处理消息的过程中,消费者的服务器在处理消息的时候出现异常,那么可能这条正在处理的消息就没有完成消息消费,数据就会丢失。为了确保数据不会丢失,RabbitMQ支持消息确定-ACK。
2):ACK的消息确认机制。
  答:ACK机制是消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除。
    如果一个消费者在处理消息出现了网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有正常消费,会将消息重新放入队列中。
    如果在集群的情况下,RabbitMQ会立即将这个消息推送给这个在线的其他消费者。这种机制保证了在消费者服务端故障的时候,不丢失任何消息和任务。
    消息永远不会从RabbitMQ中删除,只有当消费者正确发送ACK反馈,RabbitMQ确认收到后,消息才会从RabbitMQ服务器的数据中删除。
    消息的ACK确认机制默认是打开的。
3):ACK机制的开发注意事项。
  答:如果忘记了ACK,那么后果很严重。当Consumer退出时候,Message会一直重新分发。然后RabbitMQ会占用越来越多的内容,由于RabbitMQ会长时间运行,因此这个”内存泄漏”是致命的。
4):确认机制分为三种:none、auto(默认)、manual
Auto
1、如果消息成功被消费(成功的意思是在消费的过程中没有抛出异常),则自动确认
2、当抛出 AmqpRejectAndDontRequeueException 异常的时候,则消息会被拒绝,且 requeue = false(不重新入队列)
3、当抛出 ImmediateAcknowledgeAmqpException 异常,则消费者会被确认
4、其他的异常,则消息会被拒绝,且 requeue = true,此时会发生死循环,可以通过 setDefaultRequeueRejected(默认是true)去设置抛弃消息

特别提示:
如果在消费消息的时候 接收消息的时候你设置的autoAck为false 但是在消费消息完成之后没有手动ack通知服务器的后果是什么呢?
队列里面的消息会被挨个消费一遍,但是因为服务器没有收到你手动的ack消息,那么它以为失败了会自动将消费的消息再回写到队列当中去,就会导致已经消费了的消息被重复消费!所以如下图所示:

自动和手动ack(补充篇)

接收消息和消费完成之后的回执消息时候的设置要一致!
光有ack还没不行,我们还需要配合qos一起来使用,什么是qos?干什么用的?且听下文分享

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~