文章目录
RabbitMQ 的集群架构基础
RabbitMQ 网络分区引发的脑裂问题在分布式系统中相对常见,特别是当 RabbitMQ 处于集群模式(Cluster Mode)时,多个节点之间依赖网络通信来共享队列和状态信息。当发生网络分区时,集群中的节点无法通信,就可能导致脑裂现象。要理解 RabbitMQ 中的脑裂问题,需要先了解 RabbitMQ 的集群架构。
RabbitMQ 集群由多个节点组成,通常包括主节点(Master Node)和从节点(Slave Node)。这些节点负责管理和分发消息,集群模式可以提高系统的可用性、横向扩展能力以及容灾能力。RabbitMQ 的集群节点分为两类:
- 队列主节点(Queue Master):队列主节点是负责处理某个队列的节点,当客户端发送或消费消息时,它会与主节点进行交互。
- 队列镜像节点(Queue Mirror/Slave):为了保证高可用性,RabbitMQ 允许将队列的数据复制到其他节点(即镜像节点)。镜像队列的所有副本节点都维护相同的数据,但只能有一个主节点处理请求,其他副本作为备用节点。 队列的复制机制在某种程度上能够提高可用性和数据一致性,但也可能在网络分区时引发脑裂问题。
什么是MQ脑裂
MQ 脑裂问题(Message Queue Split-Brain)通常发生在分布式系统中,尤其是在消息队列(Message Queue, MQ)系统中的集群部署下。所谓“脑裂”指的是集群系统中的多个节点由于网络分区或其他原因,失去了彼此之间的通信能力,但每个节点仍然认为自己是主节点。这种情况下,集群中可能会同时存在多个主节点,它们各自独立处理相同的数据或任务,从而导致数据不一致、消息重复或丢失等问题。也称为网络分区(Network Partition)问题。
如果另一个节点在一段时间内(默认为 60 秒)无法与其联系,则节点会确定其对等方是否关闭。如果两个节点重新接触,并且都认为另一个节点已关闭,则节点将确定发生了分区。
当网络分区就位时,集群的两个(或更多!)方可以独立发展,双方都认为对方已经崩溃。这种情况被称为裂脑。队列、绑定、交换可以单独创建或删除。
检测网络分区
RabbitMQ 日志观察:
Mnesia(rabbit@warp10): ** ERROR ** mnesia_event got {inconsistent_database, running_partitioned_network, rabbit@hostname2}
可以通过服务器 日志、HTTP API(用于 监控)和 CLI 命令 识别分区的存在。
rabbitmq-diagnostics cluster_status
rabbitmq-diagnostics cluster_status 通常会显示一个空分区列表
但是,如果发生网络分区,则会显示有关分区的相关信息。
RabbitMQ 网络分区导致脑裂的原因
在 RabbitMQ 集群中,所有节点通过网络通信来维持状态的同步。当发生网络分区(即部分节点之间失去网络连接)时,可能会导致集群中的节点互相看不到对方,形成多个孤立的“分区”。这时,网络分区问题可以引发“脑裂”,具体表现如下:
• 多个节点认为自己是主节点:
如果在网络分区发生后,集群中的某些节点失去了对其他节点的通信能力,这些节点会认为自己是集群的唯一活动节点,从而继续处理消息。
网络分区的不同部分可能各自选举出新的主节点,导致集群中出现多个同时运行的主节点,这就形成了“脑裂”现象。每个分区中的主节点可能会独立接受和处理消息,这会导致数据的不一致性。
• 节点间状态不一致:
当某个队列有多个镜像节点时,镜像节点之间需要保持状态的一致性。脑裂时,多个主节点无法彼此同步消息状态,因此可能会出现同一条消息在不同节点被重复处理或丢失的情况。
脑裂恢复后,不同节点的队列状态可能已经发生变化,这可能会导致系统无法简单地将数据合并回同步一致的状态。
• 集群的不可用性和错误恢复:
网络分区发生时,部分客户端可能依旧能够连接某个节点,但由于节点无法同步状态,RabbitMQ 集群整体可能变得不可用。某些操作会卡住或超时,导致客户端连接失败或操作失败。
恢复网络连接后,如果 RabbitMQ 的仲裁机制没有妥善处理好脑裂问题,可能会导致多个主节点继续存在,或者需要手动干预才能恢复集群的正常状态。
RabbitMQ 网络分区引发脑裂的常见场景
队列镜像不同步
当网络分区发生时,不同节点的镜像队列无法互相同步状态。每个节点可能会独立处理消息,导致不同镜像之间出现状态不一致。网络恢复后,这种不一致性难以解决,可能导致消息丢失、重复消费或无法恢复到一致的状态。
HA(高可用)队列脑裂
在启用了高可用队列(HA Queues)的 RabbitMQ 集群中,所有镜像节点应该与主节点同步消息。但是在网络分区期间,镜像节点可能会认为主节点不可用,并选举自己为新的主节点。多个主节点在网络分区后各自处理同一队列,可能导致数据冲突或消息丢失。
客户端连接问题
当客户端连接 RabbitMQ 时,通常会与队列的主节点进行通信。网络分区时,客户端可能连接到不同的主节点,导致不同客户端获取到的队列状态不一致。某些客户端可能无法连接,或者会看到旧的、不正确的消息状态。此外,如果客户端连接的有问题的节点,可能出现无法连接,连接超时,无法正常发送消费等异常问题。
RabbitMQ 脑裂的影响
- 消息重复消费或丢失:由于多个主节点独立运行,它们可能会分别处理相同的消息,导致消息被重复消费或某些消息被错误丢弃。
- 数据不一致性:由于各个分区中的节点无法同步队列状态,网络恢复后,队列中可能出现未处理的消息、已经处理但未确认的消息,甚至数据冲突。
- 系统性能下降:脑裂恢复后,集群需要进行大量的数据同步操作,带来较大的性能开销,影响系统的正常运行。
- 集群不可用:由于脑裂问题的复杂性,RabbitMQ 集群可能需要人为干预才能完全恢复,导致长时间的系统不可用。
RabbitMQ 脑裂恢复办法
要从脑裂中恢复,首先选择您最信任的分区。此分区将成为系统状态(架构、消息)的权威来源;其他分区中发生的任何更改都将丢失。
停止其他分区中的所有节点,然后重新启动所有节点。当它们 重新加入集群 时,它们将从可信分区恢复状态。
最后,还应该重新启动可信分区中的所有节点以清除警告。
停止整个集群并重新启动它可能更简单;如果是这样,请确保您启动的第一个节点来自可信分区。
RabbitMQ 的自动网络分区恢复机制(Partition Handling)
RabbitMQ 提供了自动处理网络分区的策略,称为 partition_handling。可以选择不同的策略来应对脑裂,例如:
pause_minority:将网络分区中较少的节点暂停,从而避免多个主节点并存。大多数节点继续正常工作,少数节点在网络恢复后重新加入集群。RabbitMQ 会自动暂停确定自己处于少数节点(即少于总节点数的一半)的集群节点,并在检测到其他节点停止运行后。因此,它优先选择分区容忍度而不是来自 CAP 定理的可用性。这确保在发生网络分区时,至多只有一个分区中的节点将继续运行。一旦分区开始,少数节点将暂停,并在分区结束时重新启动。此配置防止脑裂,因此能够自动从网络分区中恢复,而不会出现不一致。
autoheal:自动修复脑裂问题,强制选择一个主节点,并让其他节点自动恢复并加入主节点的状态。
ignore:不处理网络分区问题,允许各个节点独立运行。这种策略可能导致脑裂后数据不一致问题。
Rabbitmq集群状态检查
通过后台检查集群状态:
partitions正常是[],出现网络分区时会有{分区节点1,[节点2,节点3]}的状态。(网络分区可以理解为“脑裂”)
running_nodes正常显示是: 集群的所有节点信息都会有,上图就只有02节点,说明02这个节点出现网络分区,只看到自己运行的节点
通过web端检查集群状态:
web端是mq服务地址的15672端口。下图可以看出rabbitmq集群出现了“Network partition”网络分区的问题了。
出现网络分区后,虽然修复了rabbitmq集群的网络分区问题,但是在网络分区期间在分区节点创建的队列消息需要去检查是否同步到其他节点。如下图,同步正常是“+2”,(集群节点数-1),即成功同步到其他节点上了。如下图:
“D ha-allqueue” 队列: D表示持久化,ha-allqueue表示所有消息都镜像复制
“AD Excl”:AD表示自动删除(auto delete),Excl表示独占(exclusive),这种队列我们就不用关心是否 “+2"了。
除了这种AD Excl状态的队列,其他如果没有”+2"的话,该队列需要手动进行同步(可以点击进入该队列信息,然后有个synchronous的按钮,手动同步)
版权归原作者 ZWZhangYu 所有, 如有侵权,请联系我们删除。