跳转到内容

在线 Aeron® 集群跨区域迁移

早晚你都要搬一个在线集群——区域迁移、换数据中心,或者当前部署已经用到瓶颈。让人安心的结论是:有了 Premium Cluster Standby,这是一份运维手册,而不是一次重建,而唯一有意义的停机,就是一次你能靠优雅让位参数压短的 leader 跨越。本页覆盖两种已验证的方式、它们的实测代价,以及那个会悄无声息破坏透明路径的隐患。

它承接 Aeron® 集群部署 收尾的地方:那页讲的是选哪种拓扑,本页讲的是如何在拓扑之间搬迁而不重建。除非另行标注,下面所有数字都是在单机上实测(3 节点集群,无 WAN)——把它们当作代价的形状,而不是对 WAN 延迟的预测。

前置状态:区域 B 里的 standby 已在 tailing

Section titled “前置状态:区域 B 里的 standby 已在 tailing”

两种方式的起点相同,而且都是纯粹的 Cluster Standby:在线的 3 节点集群跑在区域 A区域 B 里三个 standby 节点持续跨 WAN 拉取日志。standby 不投票,所以在你做准备期间,共识提交完全留在 A 本地——热路径里没有 WAN。两种方式的区别,在于你怎么把 B 从 tailing 翻转成对外服务。

逐节点(滚动)整集群切换(一次性割接)
机制通过 PremiumClusterTool transition-as-member <id> + 主机名重指向逐个替换成员;全程同一个集群把整个 B standby 组(2 参数的 transition)提升为一个集群;再重指向客户端
集群身份一个连续的集群,成员就地替换B 里一个全新的、独立的集群
步骤N 次顺序替换,每个节点都要 DNS 重指向 + 严格排序一次提升触发 + 一次客户端切换
客户端端点变更——复用主机名,客户端自动跟随——新集群,客户端必须重指向
已验证follower:——standby 加入运行中集群的槽位 k,3/3 健康。leader 跨越:在我们的单机测试台上尚未干净收敛(见下文)是——B 组组成一个集群,客户端恢复

在下面逐步走一遍两种迁移——切换场景,再用 播放下一步 看每一步。留意两个仪表:OMS → leader 延迟(面向客户端的那一跳)和共识提交路径(只有当投票多数派不在 leader 一侧时才跨越 WAN)。

逐节点 —— A 里 3 个投票成员 + B 里 3 个不投票的 standby,逐个替换
Region A
Region B
WAN ~40ms
客户
外部
gateway
网关 A
在线
网关 B
在线
oms
OMS A
活跃
OMS B
me cluster
A · 节点 0
leader
A · 节点 1
member
A · 节点 2
member
B · 节点 0
standby
B · 节点 1
standby
B · 节点 2
standby
OMS → ME leader 延迟
本地 ~0.1 ms跨区域 ~40 ms
本地
ME 共识提交路径
多数派在本地多数派需要 B(WAN)
本地
0 / 5
ME leader ME 投票成员 ME standby(不投票) 已退役 本地流量 跨区域(WAN) standby 拉取日志(tailing) 共识成员集 共识提交跨越 WAN

逐节点 —— follower 免费,leader 才是全部代价

Section titled “逐节点 —— follower 免费,leader 才是全部代价”

一次一个地把 standby 提升进成员槽位。先换 follower,最后换 leader

  • follower 替换几乎零客户端中断(实测:正好 0 条消息丢失)。leader 和多数派都留在 A;换一个 follower 对客户端是透明的。
  • 一次只动一个成员——Raft 的成员变更在设计上就是单服务器的。在一个 3 节点集群里一次换两个,会瞬间跌破多数派并停摆。预热好的 standby 让每一步都很快(只回放一小段尾部日志,而不是拉整个快照)。
  • 那唯一一次 leader 替换,才是唯一客户端可见的冲击。 移除 A 里最后一个成员——也就是 leader——会触发一次由 B 成员胜出的选举。默认且非优雅方式下,这是 ~11.8 秒(幸存者要熬过 ~10 秒的 LEADER_HEARTBEAT_TIMEOUT,再加 ~1 秒选举)。AeronCluster 客户端会根据推送来的 NewLeaderEvent 自动跟随到新 leader——无需应用代码,也不用改端点。

有一个值得点名的临时窗口:一旦三个投票成员里有两个落在 B、而 leader 还在 A,提交路径就要跨越 WAN(leader 需要一个 B 的 ack 才能凑够多数派)。推荐的顺序会通过直接进入 leader 步骤,把这个窗口压短。

整集群切换 —— 对所有人一次有界的冲击

Section titled “整集群切换 —— 对所有人一次有界的冲击”

静默或强制下线整个 A 集群,把三个 B standby 作为一组提升进一个全新集群,再重指向客户端:

  • 一次 ~7–8 秒的中断,一次性冲击所有客户端(实测,强行 fail)。没有免费的 follower 步骤——整组一起动。
  • B 组会跑一次全新的启动选举STOP_FOR_EXPORT → RECOVER_CONSENSUS_MODULE → CONSENSUS)。这里没有在任者需要让位,所以没有优雅交接 / RESIGN 的手段——它的 ~7–8 秒接近下限,不是一个可压缩的数字。
  • 客户端必须重指向。 B 是一个端点不同的不同集群,已死的 A 集群没法通告这些端点。你要把 B 的 ingress 列表显式交给客户端(配置/DNS 推送,或 switchToStandby 模式)。从构造上就对客户端不透明。
阶段逐节点整集群切换
follower 迁移~0 客户端中断(0 条消息丢失)不适用(整组一次动)
leader 转换~11.8 秒重新选举(熬过 LEADER_HEARTBEAT_TIMEOUT~7–8 秒新组的启动选举
transition 本身每节点 ~2 秒(对 follower 而言不在客户端路径上)~0.4 秒
leader 出现后的客户端切换<100 ms(自动跟随)<100 ms(切换后)
客户端可见的总中断~11.8 秒——但只在那唯一一次 leader 替换~7–8 秒,一次性冲击所有客户端
客户端端点变更

要仔细读这些总数:逐节点把工作摊到 N 步,其中只有 leader 替换是客户端可见的;整集群切换是对所有人的一次冲击。“哪个更快”归结为你能把那一次 leader 选举做到多便宜——这正是下一节的内容。

把故事补完整:让 leader 跨越变便宜

Section titled “把故事补完整:让 leader 跨越变便宜”

leader 跨越是两种方式里唯一可压缩的停机,而它和日常的 leader 再平衡是同一根杠杆——所以集群部署相关的页面早已覆盖它。这也是迁移和日常运维交汇的地方:

  • 调低 LEADER_HEARTBEAT_TIMEOUT(默认 10 秒 → 1–2 秒)——leader 空档的单一主导项,纯配置。代价是在抖动/高延迟链路上会多出误判选举。见 调优集群故障切换时间
  • 优雅让位 / RESIGN——一次显式的自愿让位会确定性地强制走快速选举路径。实测:一次计划内的交接是 调优后 ~57 ms / 默认 ~266 ms,而一次非计划死亡要付 ~11 秒——调优前便宜 ~40 倍,调优后 ~190 倍。这和你在任何故障切换后收回主节点所在可用区用的是同一套机制。完整走查:主节点放置与优先主节点控制,逐个参数的时间线见 调优优雅让位时间

有了 RESIGN,逐节点的 leader 步骤会降到 ~1–2 秒 / 亚秒级——低于整集群切换的下限——而它的 follower 步骤仍然免费。这就把默认的排名颠倒了过来:有了优雅让位,逐节点既更快对客户端透明。

DNS / JVM 缓存隐患(逐节点、复用主机名时)

Section titled “DNS / JVM 缓存隐患(逐节点、复用主机名时)”

逐节点只有在 B 节点复用 A 的主机名、且每个 JVM 都重新解析这个被重指向的名字时,才对客户端透明。这一环是关键,而且很容易做错——但隐患到底存不存在,取决于你跑的是哪种解析器:

  • Aeron 会周期性地重新解析(aeron.driver.reresolution.check.interval,默认 1 秒)。解析器是可插拔的(MediaDriver.Context.nameResolver(...));暴露程度完全看你选哪一种。
  • 用默认的 InetAddress 解析器——隐患存在。 DefaultNameResolver 调的是 InetAddress.getByName,它受 JVM DNS 缓存约束networkaddress.cache.ttl,在 SecurityManager 下会永久缓存)。一旦 JVM 缓存了旧 IP,Aeron 那个 1 秒的重解析循环就会一直拿到陈旧答案,重指向于是悄无声息地不生效——成员卡在那个已失效的 IP 上,而 Raft 不报任何错。只要在任意一个节点或客户端上漏掉 -Dnetworkaddress.cache.ttl=0,就会踩中。缓解办法是到处都设 cache.ttl=0,并为了快速割接优先用 /etc/hosts 改写(即时、本地)而不是外部 DNS。这些能减轻隐患,但去不掉。
  • 用自定义 NameResolver——隐患消除(已验证)。 一个通过 InetAddress.getByAddress(name, rawBytes) 从你自己的控制平面返回地址的解析器,根本不碰 JVM DNS 缓存,也不碰平台解析器。 一次 EC2 实测用最狠的方式证明了这点:把 JVM 缓存钉在高位(networkaddress.cache.ttl=600、并让 Aeron 的身份名字完全不进 DNS,逐节点的 follower 替换照样加入了运行中集群,重指向在 ~1 秒内生效——因为解析走的是自定义解析器,而不是 InetAddress。一个由 Consul/etcd/配置服务/文件支撑的 NameResolver,才是正经生产部署该选的。
  • 无论哪种,这都不会左右停机时间——客户端可见的停机是那次 leader 选举,它的发生和 DNS 毫无关系。解析只决定替换成员何时变得可达(也就是何时恢复到 3/3 容错)。

落到决策上:DNS 缓存隐患只有在你依赖 InetAddress/DNS//etc/hosts 时才是真问题。如果你能部署自定义解析器,它就不再是偏向整集群切换的理由。整集群切换则是从构造上绕开它:B 用的是不同的端点,所以客户端是全新连到新名字——不存在”同名换 IP”这种会被陈旧缓存搞砸的情况。(只有当你故意为 B 复用 A 的主机名时才需要在意。)

选择…当…
整集群切换你想要更简单的路径,并且能接受对所有客户端一次性、有界的 ~7–8 秒冲击。当你没有(也不想维护)RESIGN 分支时的默认选项。客户端必须重指向到 B 的端点。
逐节点你需要客户端透明的成员搬迁(复用主机名、不改端点),且 follower 以 ~0 中断迁移;并且 / 或者你能部署 RESIGN 分支(或调低心跳超时),让那唯一一次 leader 跨越做到亚秒级。代价:N 步的复杂度;同主机名的纪律(外加要么 cache.ttl=0//etc/hosts、要么一个自定义 NameResolver——后者能把 DNS 缓存隐患彻底去掉);每次替换期间一段容错降低(2/3,无备份)的窗口;以及一次非优雅时约 11.8 秒、且我们还没能证明其干净收敛的 leader 跨越(follower 步骤是免费且已验证的)。
  • 按分片逐一进行。 每个 ME 分片都是独立的集群。先迁非关键分片,观察约 5 分钟,再分批错峰迁其余的——绝不一次全上。
  • 把容错降低的窗口压短。 逐节点替换期间集群跑在 bare quorum(2/3,无备份)。在下一次替换之前,先重启每个已让位的节点、让它作为 follower 重新追平;别把已让位的节点晾着不管。
  • 网关在两个区域都保留,这样外部客户端在整个迁移期间及之后都能连到任意一个区域。

另见:Aeron® 集群部署集群 Standby 与多可用区 HA 设计客户端与集群通信NewLeaderEvent 与客户端重连),以及 主节点放置与优先主节点控制