工作中经常用到消息中间件来解决系统间的解耦问题或者高并发消峰问题,但是消息的可靠性如何保证一直是个很大的问题,什么情况下消息就不见了?如何防止消息丢失?下面通过这篇文章,我们就聊聊RabbitMQ 消息可靠性如何解决的?
本文分三部分说明
- RabbitMQ 消息丢失场景有哪些?
- 如何避免消息丢失?
- 如何设计部署消息中间件保证消息可靠性?
RabbitMQ 消息丢失场景有哪些?
首先我们看下消息周期投递过程:
我们把该图分三部分,左中右,每部分都会导致消息丢失情况,下面就详细聊聊每个阶段消息是如何丢的:
1.生产者生产消息到RabbitMQ Server 消息丢失场景
1) 外界环境问题导致:发生网络丢包、网络故障等造成RabbitMQ Server端收不到消息,因为生产环境的网络是很复杂的,网络抖动,丢包现象很常见,下面会讲到针对这个问题是如何解决的。
2) 代码层面,配置层面,考虑不全导致消息丢失
事例1:
一般情况下,生产者使用Confirm模式投递消息,如果方案不够严谨,比如RabbitMQ Server 接收消息失败后会发送nack消息通知生产者,生产者监听消息失败或者没做任何事情,消息存在丢失风险;
事例2:
生产者发送消息到exchange后,发送的路由和queue没有绑定,消息会存在丢失情况,下面会讲到具体的例子,保证意外情况的发生,即使发生,也在可控范围内。
2.RabbitMQ Server中存储的消息丢失或可靠性不足
1)消息未完全持久化,当机器重启后,消息会全部丢失,甚至Queue也不见了
假如:你仅仅持久化了Message,而Exchange,Queue没有持久化,这个持久化是无效的。
记得之前公司有一哥们忘记持久化Queue导致机器重启后,Queue不见了,自然Message也丢失了。
2)单节点模式问题,如果某个节点挂了,消息就不能用了,业务可能瘫痪,只能等待
如果做了消息持久化方案,消息会持久化硬盘,机器重启后消息不会丢失;但是还有一个极端情况,这台服务器磁盘突然坏了(公司遇到过磁盘问题还是很多的),消息持久化不了,非高可用状态,这个模式生产环境慎重考虑。
3)普通集群模式:某个节点挂了,该节点上的消息不能用,有影响的业务瘫痪,只能等待节点恢复重启可用(建立在消息持久化)
虽然这个模式进步了一点点,多个节点,但是消息还是不能保证可靠,为什么呢?
因为RabbitMQ 集群模式有点特殊,队列的内容仅仅存在某一个节点上面,不会存在所有节点上面,所有节点仅仅存放消息结构和元数据(可以理解为索引,这也是为了提高性能,如果每次把所有内容同步到所有节点是有开销代价的)。
下面自己画了一张图介绍普通集群丢失消息情况:
这里有三个节点,通常情况下一个磁盘节点,两个内存节点,首先先说明下, Queue1 内容仅仅存在节点note1上面,在创建队列的时候已经固定了,note2,note3 仅仅存放的是元数据,这个一定要清楚,Producer发送消息到note2,note2 会同步元数据到其他节点,内容会同步note1。
那我们想下,图中的Q1问题,note1挂了,这个节点的Queues全部暂时不可用,节点恢复后可用。
我们说下图片中备