bigywater.tech
RabbitMQ · 16

quorum 队列的 Raft:选主与写入路径

quorum 队列每个队列是一个 Raft 组:leader、follower、term、日志下标到底长什么样?杀掉 leader 选举要多久?只剩一个节点时发布和读取各是什么表现?这篇用 rabbitmq-queues quorum_status 一步步看。

Term 1→2→3
每次选举 Term 加 1;杀 leader 一次、恢复时再选一次
360 ms
kill -9 掉 leader 后,quorum 队列重新能带 confirm 发布的时间
1002 条
无多数派时挂起的发布恢复后也得到 ack,一条没丢

Raft 组

实测 · 本机执行输出
  $ rabbitmq-queues quorum_status rq
    节点    Raft 状态        Term  Last Log  Commit  Applied
    n1    leader            1         2       2        2
    n2    follower          1         2       2        2
    n3    follower          1         2       2        2

每个 quorum 队列有自己的一个 Raft 组:一个 leader 和若干 follower(默认三个成员)。rabbitmq-queues quorum_status 给出每个成员的 Raft 状态、Term 和日志下标(Last Log、Commit Index、Last Applied)。

写入路径

实测 · 本机执行输出
    节点    Raft 状态        Term  Last Log  Commit  Applied
    n1    leader            1      1003    1003     1003
    n2    follower          1      1003    1003     1003
    n3    follower          1      1003    1003     1003
  当前 leader: n1@localhost

发布的消息先到 leader,作为日志条目复制给 follower,多数派落盘后提交,提交后才回 publisher confirm。平静时三个成员的下标一致。

选举

实测 · 本机执行输出
  杀掉 leader 后,第一次成功发布(confirm 回来)在 360 ms 之后
  list_queues: rq | n3@localhost | [n3@localhost, n2@localhost]
    节点    Raft 状态        Term  Last Log  Commit  Applied
    n1    noconnection                                  
    n2    follower          2      1008    1008     1008
    n3    leader            2      1008    1008     1008

leader 所在节点被杀后,剩下的成员发现没有心跳,发起选举,Term 加 1,拿到多数票的成为新 leader,本次实测 360 ms 内恢复发布。

没有多数派

实测 · 本机执行输出
  只剩 1/3 个节点时发布一条(带 confirm),等 8 秒: 仍在等待 confirm,没有 ack 也没有 nack
  同样条件 basic_get 取一条: ConnectionClosedByBroker: (541, "INTERNAL_ERROR - Cannot get a message from queue 'queue 'rq' in vhost '/'

三个成员只剩一个时写入一直挂起(没有 ack 也没有 nack),读取直接报错。quorum 队列选择一致性。

恢复

实测 · 本机执行输出
  之前挂起的那条发布最终结果: ack
    节点    Raft 状态        Term  Last Log  Commit  Applied
    n1    follower          3      1015    1015     1015
    n2    follower          3      1015    1015     1015
    n3    leader            3      1015    1015     1015
  队列里最后共有 1002 条;另外之前挂起的 basic_get(auto_ack)在恢复后取走了 0 条——共发布 1000 + 1(选举后)+ 1(无多数派时)= 1002 条

节点回来、追平日志后,挂起的发布得到 ack,数据一条不丢。

代价

实测 · 本机执行输出
  rq1: members = [n2@localhost]
  rq3: members = [n2@localhost, n1@localhost, n3@localhost]
  rq1: 333 ms  60126 条/秒
  rq3: 396 ms  50514 条/秒

三成员比单成员慢了约 19%(396 对 333 ms 发布 20000 条),在本机回环上。

分步演示

quorum 与 Raft
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / RabbitMQ 4.3.6 / Erlang/OTP 28.5 / pika 1.4.4(Homebrew 与临时虚拟环境);所有节点都是本机自己起的 RabbitMQ 进程(127.0.0.1,端口 9500 以上,每个实验独立的数据目录与节点名,不连任何外部服务);命令输出来自真实的 rabbitmqctl 与管理 API,发布与消费用 pika 的真实连接;计时用 time.perf_counter,数字每次运行会有小幅抖动。集群是 3 个本机节点;quorum_status 用 rabbitmq-queues --formatter json 取出后自己排版;选举时间是循环重试带 confirm 发布的第一次成功;无多数派的试验杀了两个节点,发布与 basic_get 各等 8 秒;吞吐是 pika SelectConnection 异步 confirm 发布,3 轮取中位数
    • 没有验证:网络分区(而不是节点被杀)下的行为;Raft 日志的 snapshot / 截断与 WAL、segment 文件;选举超时的参数调整;多次选举时间的分布(只测了一次 360 ms);大消息、多队列并发时 WAL 的写入合并;成员加减(grow / shrink)的过程
    京ICP备2026067502号-1京公网安备11010502063487号