跳转到内容

调优集群故障切换时间

Aeron® Cluster 的 leader 故障切换时间完全可调 —— 默认值很慢。开箱配置从一次 leader 丢失中恢复要 花 约 11 秒,几乎全耗在一个超时上。你可以把它压到 亚秒级,但这根旋钮是双刃的:拧得太紧, 一个只是短暂沉默的 健康 leader 就会被无缘无故投票下台。多数部署把 aeron.cluster.leader.heartbeat.timeout 设成 1 s 就够了:故障切换约 1.5 s,寻常的 GC 和调度 停顿也扛得住。

选举的内部机制(term、canvass、投票记录)见 The Aeron Files。新 leader 落在哪里,见 Leader 放置与优雅让位。

Aeron Cluster 的故障切换时间由什么构成

Section titled “Aeron Cluster 的故障切换时间由什么构成”

故障切换 = 检测(察觉 leader 不在了)+ 选举(投出新 leader)+ 客户端重连(客户端把 ingress 重新指向新 leader)。从 kill 那一刻算起:

检测与选举 串行 进行,所以下限大致是 heartbeat.timeout + election.timeout + relink。

属性默认值均衡(推荐)激进(已测)作用说明
aeron.cluster.leader.heartbeat.timeout10,000 ms1,000 ms250 ms检测 —— follower 在收到 leader 最后一次心跳后,等多久才宣告它已死占开箱故障切换时间的约 90%;最大的一根杠杆
aeron.cluster.leader.heartbeat.interval200 ms200 ms50 msleader 多久发一次心跳必须远小于 timeout(一个窗口内最好有 ≥3–5 拍)
aeron.cluster.election.timeout1,000 ms500 ms300 ms检测之后的投票/选举阶段在检测 之后 串行进行
aeron.cluster.election.status.interval100 ms50 ms25 ms选举轮次节奏
aeron.cluster.session.timeout10,000 ms——客户端 会话存活与 leader 故障切换无关;决定集群多久驱逐一个沉默的客户端
aeron.cluster.startup.canvass.timeout60,000 ms——冷启动 leader canvass与故障切换无关(仅启动时)

默认情况下这些故障切换旋钮一个都没设,所以开箱的 10 s 检测主宰了一切。

  • 从真实 leader 丢失中恢复更快。 最直接的收益:我们测试中从 ~11 s → ~0.85 s(约 13×)。真崩溃 后,数据面停顿和背压的时间更短。
  • 客户端可见的中断更短。 整个故障切换窗口内客户端 ingress 都被背压,窗口越短,吞吐缺口越小,恢复 时要排空的积压也越少。
  • SLA 更紧、更可预期。 若必须承诺”在 N 秒内恢复”,调低超时是缩小 N 的唯一办法。
  • 更早释放被占住的资源。 绑在死 leader 上的订阅、会话状态、流控窗口能更快清干净。

aeron.cluster.leader.heartbeat.timeout 是一个 dead-man’s switch。只要一个 健康 的 leader 沉默超过窗口 —— 无论 什么 原因 —— follower 就会宣告它已死并跑一次 真实的选举:一次货真价实的 ~0.85–1.5 s 停顿外加 客户端背压,纯属自找,其实压根没出故障。窗口越紧,会兜住的良性停顿就越多。

会在健康 leader 上触发过紧窗口的停顿来源:

停顿来源典型时长缓解手段
GC 暂停ZGC 亚毫秒;G1/Parallel 或大堆 → 100 ms–数秒低暂停 GC(ZGC)
CPU 调度 / 抢占若共享核则 ms–数百 msisolcpus + nohz_full 绑核
Archive fsync / 磁盘卡顿网络存储上 10–数百 msarchive 放在快速本地存储 / tmpfs
网络抖动 / 心跳丢包µs–ms同 AZ 放置组(低 RTT、无丢包)
VM steal / 吵闹邻居ms–数百 ms裸金属(无 hypervisor steal)
Safepoint / JIT / THP 整理ms–数百 ms(残余尾部风险)
  • interval 与 timeout 之比。 heartbeat.interval 必须远小于 heartbeat.timeout(一个窗口内争取 ≥3–5 拍)。只调低 timeout 却不调 interval,单个 延迟或丢失的包就会触发一次误切换。这是最常见的 配置错误。
  • 选举反复。 election.timeout 太短时,一次本该多花点时间的正当选举(canvass、日志追赶)可能自己 就超时重启 → 选举 来回打转,反而让有效故障切换 更长 甚至不收敛 —— 与目标背道而驰。
  • 负载下的 leader 抖动。 高负载下过紧的超时会让 leader 在节点间来回弹跳,每弹一次都逼着客户端重连 并换一个 term。反复抖动的集群,可用性可能 不如 一个慢但稳的故障切换。
  • 短 soak 带来的虚假信心。 几分钟、零抖动的运行,不能证明它能连续几天安然无恙 —— 生产环境会 叠加起伏的负载、长时运行才冒头的尾部 GC 事件、以及突发流量。信心需要 长时 soak(数十分钟到 数小时),不是短促的基准。

调低超时破坏不了什么:已提交数据与单 leader 安全

Section titled “调低超时破坏不了什么:已提交数据与单 leader 安全”

调低超时,是把一种失败模式(恢复慢)换成另一种(自找的停顿)。它并 不会 引入正确性隐患:

  • 不丢数据。 Aeron Cluster 的提交基于 quorum(Raft);已提交的数据无论时序如何都能挺过故障切换。见 Cluster 与 Raft 概览。
  • 不会脑裂。 新 leader 需要 多数 票,所以一次误选举不可能产生两个 leader。误切换造成的损害纯粹 是一次 可用性抖动 —— 绝不会是数据损坏或双写者。

实测故障切换时间(Aeron 1.48.11,裸金属)

Section titled “实测故障切换时间(Aeron 1.48.11,裸金属)”

在裸金属机群(Granite Rapids,SNC-3)上实测,800K tps、128B 消息、MTU 8192,采用”负载下 kill leader”的测试和一个 具备故障切换感知的客户端(实现了 EgressListener.onNewLeader 并调用 pollEgress())。基于 Aeron 1.48.11 验证;属性名与默认值通过反射运行中的 jar 确认。

开箱超时 → ~11 s,以及那个客户端陷阱

Section titled “开箱超时 → ~11 s,以及那个客户端陷阱”

用开箱超时时,选举本身是健康的 —— 节点被 kill → 选出新 leader,0 NAK / 0 重传 / 0 错误,幸存 的 follower 什么都没丢(它在 kill 时已完整复制,故晋升无需重新同步;新 leader 记录了 2 个良性的追赶 NAK)。

实测真实故障切换 ≈ 11.0 s —— 三个独立信号相互吻合(新 leader 的 raft-log 发送位置、客户端字节恢复、 客户端 NEWLEADER 事件)。正好是开箱的 10 s 心跳超时 + ~1 s 选举,与模型预测完全一致。

激进超时 → ~0.85 s,且不设下限

Section titled “激进超时 → ~0.85 s,且不设下限”

设 heartbeat.timeout=250ms、heartbeat.interval=50ms、election.timeout=300ms、 election.status.interval=25ms —— Aeron 原样接受了每一个值;不存在强制下限。

试次检测(ms)选举(ms)客户端重连(ms)总计(ms)误切换
13376377648090
23386958088330
33398059029470
中位数3386958088330
  • 稳定在 ~0.85 s —— 比开箱快约 13×,所有客户端都完整恢复。
  • 没能进到 500 ms 以内,这是结构性的: 检测(~338 ms ≈ heartbeat.timeout)与 election.timeout (300 ms)串行 进行 → 在重连之前就已耗掉 ~640 ms 选出 leader。要压到 500 ms,两项都得缩,而这 会侵蚀抖动余量。
  • 误切换 = 0,横跨约 28,000 个稳态采样 —— election_state=CLOSED,整个 kill 前窗口角色保持不变。 250 ms / 50 ms 在这台机器上不抖动(低暂停 GC、isolcpus、快速本地 archive、同 AZ)—— 恰恰印证了 上面的告诫:它在 这里 安全。

随着你把 aeron.cluster.leader.heartbeat.timeout 拧紧,两项代价的变化速率截然不同:

  • 恢复速度的收益在 ~2 s 以下几乎持平 —— leader 故障本就 罕见,1.5 s 与 0.85 s 用户很少能感知 差别。
  • 误切换风险随拧紧而陡增 —— 这是 持续 的敞口(每次 GC 暂停、safepoint、丢包延迟,每天每 秒都在)。粗略地说,超时减半,兜住良性停顿而误触发选举的概率就翻倍。

从 2 s → 0.85 s 的边际收益很小,边际风险却很大。这就是 曲线的拐点。

档位超时故障切换抖动余量(可容忍的 leader 停顿)何时用
保守开箱(10 s / 1 s)~11 s~10 s(从不抖动)你从不 kill leader / 批处理系统
均衡(推荐)hb 1000 / int 200 / elec 500 / stat 50~1.5 s~1 s —— 覆盖几乎所有 GC/safepoint/调度尾部;对非理想环境也稳健多数生产部署
激进(已测)hb 250 / int 50 / elec 300 / stat 25~0.85 s~250 ms —— 单次 G1 暂停 / THP 整理就可能超过硬性亚秒 SLA、理想机器、且做过 soak 之后

推荐做法:

  1. 默认选均衡的 ~1–2 s 档(heartbeat.timeout=1000ms、interval=200ms、election.timeout=500ms、 election.status.interval=50ms)。它比开箱快约 7×,保留了激进档约 4× 的抖动余量,即便环境偏离理想也 仍然安全。
  2. 保持 interval ≪ timeout(一个窗口 ≥3–5 拍)。绝不要只调低 timeout。
  3. 只在有硬性 SLA 时才进亚秒级,且只在 数十分钟到数小时的 soak(贴近生产的负载 以及 堆/GC、并统计误切换次数)之后。安全下限由你最坏的真实健康-leader 停顿决定,不是你希望的那个数字。
  4. session.timeout 是独立的 —— 按”集群该多快驱逐一个沉默客户端”来调它,与 leader 故障切换无关。
  5. 上一个具备故障切换感知的客户端(pollEgress() + EgressListener)—— 没有它,任何故障切换时间 都毫无意义。

在生产中调低故障切换时间前的检查清单

Section titled “在生产中调低故障切换时间前的检查清单”
  • 用的哪个 GC,它在负载下的最坏暂停是多少?(ZGC ≪ G1/Parallel。)这决定你的下限。
  • 热线程是否绑核(isolcpus/nohz_full),还是在抢核?
  • archive 往哪儿 fsync —— tmpfs、本地 NVMe、还是网络存储?(磁盘卡顿会触发检测。)
  • 同 AZ / 放置组,还是跨 AZ?(网络抖动会拉宽所需余量。)
  • 裸金属还是 VM?(VM steal 会叠加尾延迟。)
  • interval 是否 ≤ timeout 的约 1/5?
  • 是否在生产级负载 以及堆/GC 下 soak 足够久,看到尾部(≥ 数十分钟)?
  • ingress 客户端是否处理 onNewLeader 并轮询 egress?
  • 这个目标是真实 SLA 撑起来的,还是 1–2 s 其实就已经够好了?

延伸阅读:优雅让位耗时调优(计划内交接那条预算 —— 参数是另外一套)、Leader 放置与优雅让位、 Cluster Standby 与多 AZ HA 设计、以及 故障模式运维手册。

本站与 Adaptive Financial Consulting Limited 或 Aeron 项目无任何关联,未获其背书或赞助。 Aeron 是 Adaptive Financial Consulting Limited 的注册商标。

Aeron 是 Adaptive Financial Consulting Limited 在英国及其他国家/地区的商标。