前面两篇讲了集群和元数据存储,这一篇讲集群最棘手的故障——网络分区(network partition)。这是所有分布式系统都要面对的问题,RabbitMQ 因为历史原因(Mnesia、镜像队列)在这个问题上格外敏感。

虽然 Khepri 和 Quorum Queue 用 Raft 从架构层面缓解了分区问题,但理解分区的表现和处理策略,仍然是 RabbitMQ 运维的核心能力。因为存量系统(3.x 及更早)仍然广泛存在,且即便在 4.0,集群层面的分区检测和处理策略仍然重要。

网络分区是什么

网络分区是指:集群节点之间的网络中断,但节点本身还在运行。结果是集群被「分裂」成两个或多个互相不可达的子组,每个子组以为对方挂了。

分区的诱因很多:

  • 物理网络故障(交换机、网线、路由器问题)。
  • 防火墙规则变更、安全组配置错误。
  • 节点负载过高,网络响应超时被误判为断开。
  • 虚拟化/云环境的网络抖动。

分区的危险在于:每个子组可能各自继续提供服务,导致数据不一致。比如两边各自接收写入、各自修改元数据,分区恢复时数据冲突,难以合并。这就是「脑裂」——一个系统里出现两个「大脑」各自决策。

RabbitMQ 怎么检测分区

RabbitMQ 的分区检测基于 Erlang 的节点间心跳。节点间定期互发心跳,如果一段时间(默认约 60 秒)收不到对方心跳,就认为对方「不可达」,标记为分区状态。

检测到分区后,RabbitMQ 会在管理界面和日志里显示分区告警(partitions 状态)。此时集群处于「逻辑分裂」状态。

但「检测到」不等于「处理好」。RabbitMQ 怎么应对分区,取决于配置的分区处理策略(partition handling strategy)。这是关键的运维决策。

三种分区处理策略

RabbitMQ 提供三种分区处理策略(通过 cluster_partition_handling 配置):

1. ignore(默认,不推荐生产用)。 检测到分区后什么都不做,各子组继续运行。分区恢复时,各子组尝试自动合并,按某种规则丢弃冲突的变更。

这是最危险的策略——分区期间各子组独立写入,恢复时必然丢数据。它适合「分区几乎不会发生、且丢了也能接受」的场景,生产环境绝不该用。

2. pause_minority(推荐)。 检测到分区后,少数派子组自我暂停(suspend),停止对外服务。多数派子组继续运行。分区恢复后,少数派重新加入,从多数派同步状态。

这是最安全的策略。它保证任何时候只有「多数派」在服务,从根本上避免双写脑裂。代价是少数派节点在分区期间完全不可用(连不上)。对可用性影响最小的部署是「跨 3 个 AZ 的 3 节点集群」——任意一个 AZ 故障,剩余两个 AZ 仍是多数派,服务不中断。

3. autoheal。 检测到分区后,各子组继续运行。分区恢复时,按「客户端连接数」等规则决定哪个子组「赢」,输的子组被重启、状态被丢弃。

autoheal 试图在分区期间保持可用性(各子组都服务),但代价是恢复时明确丢数据(输的一边被重启丢弃)。它不如 pause_minority 安全,适合「可用性优先于一致性」的场景。

为什么 pause_minority 是推荐的

三种策略里,pause_minority 是生产环境的标准推荐,原因在于它的语义最清晰、最安全:

  • 分区期间,只有多数派能服务。这意味着任何写入都发生在「多数节点同意」的前提下,天然避免脑裂。
  • 少数派暂停,不会产生冲突的写入。
  • 恢复时,少数派从多数派同步,状态一致。

它的哲学是**「宁可不可用,不可不一致」**(CP)。对于消息队列这种「丢一条消息就是事故」的系统,一致性优先于可用性是正确的取舍。短暂不可用(少数派暂停)可以靠客户端重试、负载均衡切换到多数派来缓解;而数据不一致(脑裂)一旦发生,修复成本极高且可能无法完全恢复。

pause_minority 的前提是集群节点数为奇数(3、5、7),这样才有明确的「多数」概念。偶数节点(如 4)在 2-2 分区时两边都凑不够多数,会全部暂停,服务完全中断。这也是为什么集群要用奇数节点。

Quorum 和 Khepri 时代的分区表现

上面的分区处理策略,主要针对的是 Mnesia + 经典/镜像队列时代的 RabbitMQ。随着 Quorum Queue 普及(4.0 移除镜像队列后成为唯一高可用队列方案)和 Khepri 逐步成为默认(4.2 起),分区的表现有了变化:

Quorum Queue 在分区下:Quorum 本身基于 Raft,分区时少数派副本无法参与写入,多数派副本继续选 leader、服务。分区恢复后少数派自动同步。所以 Quorum Queue 自带分区容错,不依赖集群级的 pause_minority——它自己就是 CP 的。

Khepri 在分区下:同样基于 Raft,元数据操作只有多数派能提交。少数派无法修改元数据。

所以严格说,在「全 Quorum + Khepri」的集群里(4.2 默认之后,或 4.0/4.1 手动启用 Khepri 的部署),即使分区处理策略是 ignore,消息和元数据的一致性也有 Raft 保证。但集群层配置 pause_minority 仍然是推荐做法,因为它能在分区时让少数派整体暂停,避免少数派上的经典队列(如果还有)和非 Raft 组件出问题。多一层保险。

分区恢复后的检查

分区恢复后,运维要检查几件事:

  • 分区告警是否清除(管理界面或 rabbitmqctl cluster_status)。
  • 各节点状态是否一致,特别是元数据。
  • 队列是否正常,Quorum 队列的 leader 是否恢复、副本是否同步。
  • 应用层是否有数据问题,如果是 autoheal 或 ignore 策略,可能有数据丢失或重复,要核对业务。

三种分区处理策略对比

收束:分区是分布式系统的必修课

网络分区不是「会不会发生」的问题,而是「什么时候发生」的问题。任何跨网络的分布式系统都要面对它。RabbitMQ 的经验是:用 Raft(Quorum + Khepri)从架构层保证一致性,用 pause_minority 在集群层做兜底。理解这套防线,才能在分区发生时知道系统的行为边界,而不是手忙脚乱。

下一篇讲跨机房容灾——Federation 和 Shovel,那是比集群更高一层的、解决「地理距离」问题的机制。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。我相信 AI 是程序员的最佳搭档。想跟完这套「图解 RabbitMQ」,欢迎关注公众号 「十三Tech」

十三Tech公众号二维码