目前正在测试中:完成后将开放 GitHub 代码。

Raft 参数

项目
心跳间隔500ms(cluster_heartbeat_ms)
无响应判定时间2000ms(cluster_election_timeout_ms)—— 心跳中断达到该时长即判定 leader 已死
实际发起选举的区间1750-2000ms —— 每个节点随机错开,以避免同时成为候选者(split vote)。最坏值恰好等于你配置的无响应判定时间
Leader 故障时客户端的停顿实测为无响应判定时间 + 0.3-0.5 秒(默认配置下约 2.3-2.5 秒)—— 即检测时间加一次重定向
节点数3 或 5(在加载配置时强制校验 —— 其他数值一律拒绝启动)
节点 ID其(cluster_self)在 cluster_peers 列表中的位置 —— 这也是为什么列表顺序在每个节点上必须完全一致
引导启动启动后 500ms,每个节点都以相同的成员关系初始化 —— 若加入的是已初始化的集群,则会被忽略
日志存储内存中(易失)—— 重启的节点通过复制/快照恢复
Lease 过期检测leader 每 100ms 检查一次,并通过共识提交 Expire
wait 超时粒度等待超时(T)也是在同一个 100ms 周期上决定的 —— 最多可能晚到 100ms
Leader 变更通知leader 变更时,会立即向所有已连接的客户端发送 L

如何选择心跳与无响应判定时间

这两个值通过配置中的 cluster_heartbeat_mscluster_election_timeout_ms 调整。无响应判定时间(T)就是 leader 故障时服务停顿的 时长;反过来,设置得太短又会把活着的 leader 误判为已死,引发不必要的选举。

心跳 / T检测区间Leader kill 时的最坏延迟(实测)推荐环境
100ms / 600ms450-600ms约 0.9 秒同机架、同可用区,延迟非常稳定
100ms / 1000ms750-1000ms约 1.2 秒同可用区
250ms / 2500ms1875-2500ms约 2.8 秒多可用区
500ms / 2000ms1750-2000ms约 2.4 秒默认值 —— 多可用区下的平衡点
500ms / 5000ms3750-5000ms约 5.3 秒跨地域、延迟抖动较大的环境
  • 平常时的处理延迟与这些值无关(实测 p50 无变化)—— 它们只影响故障时的恢复时间。
  • follower 挂掉时,无论取什么值都不会影响客户端。上述延迟只在 leader 挂掉时才会出现。
  • 约束:cluster_election_timeout_ms 至少为 600ms,且至少是心跳的 4 倍。 (已实测到:负载中一次调度延迟就能整整吞掉两次心跳。)

为什么是 3 或 5 个节点

提交需要多数派。偶数节点配置只会增加成本,却不能提升容错能力, 因此服务器拒绝以偶数节点启动。

节点数多数派可容忍的并发故障数
220 —— 单个故障就会使集群停止服务。并不比单机模式更好
321
431 —— 与 3 节点相同,只是成本更高
532

故障行为总结

情况行为
客户端向非 leader 节点发送请求M(leader 地址),若 leader 未知则返回 E no_leader
Leader 故障在无响应判定时间内(默认配置为 1750-2000ms)选出新 leader。此窗口期内的请求会收到 E no_leader → 客户端重试
Leader 变更期间待处理的获取请求会以 M/E no_leader 清空,客户端向新 leader 重试
节点重启以空状态启动 → 通过对端的日志复制/快照追赶进度
多数派丢失提交变得不可能 → 停止写入(安全第一),多数派恢复后自动继续

术语

  • 多数派 (quorum) —— 超过全部节点半数(3 个中的 2 个,或 5 个中的 3 个)。 由于任何决策都需要多数派同意,两个被分区的组永远不可能同时提交相互冲突 的决策。
  • 无响应判定时间 (election timeout) —— follower 在未收到心跳的情况下等待多久 才断定 leader 已挂掉并发起选举。每个节点上的具体时长是随机化的, 以减少同时发起候选的情况。