RabbitMQ消息丢失场景
生产者消息丢失
- 外界环境问题导致:发生网络丢包、网络故障等造成RabbitMQ Server端收不到消息,因为生产环境的网络是很复杂的,网络抖动,丢包现象很常见
- 一般情况下,生产者使用Confirm模式投递消息,如果方案不够严谨,比如RabbitMQ Server 接收消息失败后会发送nack消息通知生产者,生产者监听消息失败或者没做任何事情,消息存在丢失风险;
- 代码层面,配置层面,考虑不全导致消息丢失
- 生产者发送消息到exchange后,发送的路由和queue没有绑定,消息会存在丢失情况,下面会讲到具体的例子,保证意外情况的发生,即使发生,也在可控范围内。
MQ存储的消息丢失或可靠性不足
- 消息未完全持久化,当机器重启后,消息会全部丢失,甚至Queue也不见了
- 单节点模式问题,如果某个节点挂了,消息就不能用了,业务可能瘫痪,只能等待
- 如果做了消息持久化方案,消息会持久化硬盘,机器重启后消息不会丢失;但是还有一个极端情况,这台服务器磁盘突然坏了(公司遇到过磁盘问题还是很多的),消息持久化不了,非高可用状态,这个模式生产环境慎重考虑。
- 普通集群模式:某个节点挂了,该节点上的消息不能用,有影响的业务瘫痪,只能等待节点恢复重启可用(建立在消息持久化)
- RabbitMQ 集群模式有点特殊,队列的内容仅仅存在某一个节点上面,不会存在所有节点上面,所有节点仅仅存放消息结构和元数据
- 镜像模式:可以解决上面的问题,但是还是有意外情况发生
- 比如:持久化的消息,保存到硬盘过程中,当前队列节点挂了,存储节点硬盘又坏了,消息丢了,怎么办?
消费者消息丢失
消费端接收到相关消息之后,消费端还没来得及处理消息,消费端机器就宕机了,此时消息如果处理不当会有丢失风险。
如何避免消息丢失?
下面也是从三个方面介绍:
1.生产者消息如何不丢
2.MQ中存储的消息如何保证
3.消费者消息如何不丢
生产者消息丢失
- 外界环境问题导致:发生网络丢包、网络故障等造成RabbitMQ Server端收不到消息,因为生产环境的网络是很复杂的,网络抖动,丢包现象很常见
- 一般情况下,生产者使用Confirm模式投递消息,如果方案不够严谨,比如RabbitMQ Server 接收消息失败后会发送nack消息通知生产者,生产者监听消息失败或者没做任何事情,消息存在丢失风险;
- 代码层面,配置层面,考虑不全导致消息丢失
- 生产者发送消息到exchange后,发送的路由和queue没有绑定,消息会存在丢失情况,下面会讲到具体的例子,保证意外情况的发生,即使发生,也在可控范围内。
问题1解决方案
- AMQP协议提供的事务机制
- 生产者消息发送确认机制
- 开启确认模式,设置回调函数,当消息发送到broker时触发回调方法,通过ack判断当前消息是否成功发送到交换机,如果失败,则需处理。
问题2解决方案
- 使用备份交换机(alternate-exchange),处理没有路由到队列的消息
- 生产者消息发送回退机制
- 开启回退模式,设置回调函数,当消息从exchange路由到queue失败后,如果设置了rabbittemplate.setMandatory(true)参数,则消息退回给producer,并执行回调方法。
消费者消息丢失
消费端接收到相关消息之后,消费端还没来得及处理消息,消费端机器就宕机了,此时消息如果处理不当会有丢失风险。
解决方案
消费者关闭自动ack应答,手动ack应答。
MQ存储的消息丢失或可靠性不足
当我们解决了,生产端和消费端的问题后,基本保证消息的不丢问题,但是还有一个是消息的高可用问题,单节点问题,普通节点的问题都会影响消息的临时不可用,这个时候要用上我们的 HA 镜像集群模式来保证。
使用集群模式+镜像队列;
消息补偿机制
为什么还要消息补偿机制呢?难道消息还会丢失,没错,系统是在一个复杂的环境,不要想的太简单了,虽然以上方案,基本可以保证消息的高可用不丢失的问题,但是作为有追求的程序员来讲,要绝对保证我的系统的稳定性,有一种危机意识。
比如:持久化的消息,保存到硬盘过程中,当前队列节点挂了,存储节点硬盘又坏了,消息丢了,怎么办?

- 业务数据入库
发送消息前,先将消息入库。
- 发送消息
正常步骤:发送消息到队列Q1,consumer监听到Q1的消息后,消费消息处理业务并发送消息到队列Q2,回调检查服务监听到Consumer反馈的确认消息后,将消息写入数据库MDB;这就是消息发送成功的正常情况。
失败步骤:如果消息发送到consumer失败,那么consumer消费不到消息,consumer的消息入库操作也失败了。这时不用担心,在发送消息后过几分钟会进行延迟发送消息给Q3,回调检查服务监听到延迟消息后,与MDB中消息进行对比,MDB中没有这条消息,这说明consumer消费消息失败了,那么回调检查服务就远程调永produce重新进行发送消息,那么可以重新进行上面消息发送的步骤。 - 发送消息失败,发送延迟消息也失败了,几率很小。
这时也不用担心,定时检查服务会比对MDB数据库与业务数据库的差别,然后让producer重新发送缺失的消息。
产线网络环境太复杂,所以不知数太多,消息补偿机制需要建立在消息要写入DB日志,发送日志,接受日志,两者的状态必须记录。然后根据DB日志记录检查发送消费是否成功,不成功,进行消息补偿措施,重新发送消息处理。