news 2026/10/12 3:47:59

orchestrator/raft 共识集群:MySQL 拓扑管理的高可用与跨数据中心隔离方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
orchestrator/raft 共识集群:MySQL 拓扑管理的高可用与跨数据中心隔离方案
  • 后端
  • 数据库

【免费下载链接】orchestrator

MySQL replication topology management and HA

项目地址:https://gitcode.com/gh_mirrors/or/orchestrator
点击查看免费下载

orchestrator/raft是 orchestrator 的一种部署形态:多个orchestrator服务节点通过 Raft 共识协议组成一个集群,由获得法定人数(quorum)的节点选举出唯一 leader 来执行变更。本指南围绕 docs/raft.md 展开,先讲清 Raft 集群的工作原理与节点、后端数据库、代理、orchestrator-client四大组件的部署细节,再结合源码解释健康检查、反向代理、命令分发、快照回放等底层机制,并给出跨数据中心网络隔离(fencing)的实战场景,帮助你为 MySQL 复制拓扑管理搭建一套"自身高可用 + 防脑裂"的架构。

orchestrator/raft 是什么:为 orchestrator 自身解决高可用与脑裂问题

常规的 orchestrator 部署依赖一个共享后端数据库(shared backend)来保证多实例的一致性;而orchestrator/raft则完全不同:若干个orchestrator节点之间通过raft共识协议互相通信,共同维护一份有序的命令日志(raft log),只有获得 quorum 的节点才能成为 leader 并执行变更。

这种形态同时解决两类问题:

  1. orchestrator 自身的高可用:单节点故障不影响整体可用性,leader 故障后会自动重选;
  2. 网络隔离 / 跨数据中心分区(fencing):借助 quorum 机制,被网络分区隔离出去的节点不可能成为 leader,因此永远不会出现"两个 orchestrator 同时操作同一个 MySQL 拓扑"的脑裂局面。

关于orchestrator/raft与另一条高可用路线(同步复制后端,如 Galera/XtraDB Cluster/InnoDB Cluster)的详细对比,可参阅 docs/raft-vs-sync-repl.md。

Raft 共识的本质:quorum 保证"leader 不在隔离区"

Raft 的核心特性是:节点通过投票选出一个具有quorum(法定人数)的 leader,quorum 意味着该节点没有被网络隔离。

  • 在3 节点集群中,quorum 大小为2;
  • 在5 节点集群中,quorum 大小为3。

举例来说,假设n1、n2、n3三个节点组成集群,正常情况下会稳定选出一个 leader。若网络分区把n1与n2、n3隔开,则 leader 必然出自n2或n3——n1因凑不齐 quorum(3 节点需 2 票)而无法当选。源码中 quorum 的计算逻辑见 go/raft/raft.go#L238-L244:

func QuorumSize() (int, error) { peers, err := GetPeers() if err != nil { return 0, err } return len(peers)/2 + 1, nil }

这一特性在跨数据中心(DC)场景下极其有用:假设你在三个 DC 各部署一个orchestrator节点,一旦某个 DC 被网络隔离,活跃的 leader 必然位于隔离区之外、拥有共识的节点上——被隔离 DC 里的 orchestrator 即便还活着,也无法执行任何拓扑变更。

部署架构与关键技术细节

节点规模:3 或 5(推荐)

官方推荐部署3 或 5 个orchestrator节点,其他数量也合法,但至少需要 3 个。容错能力与节点数直接挂钩:3 节点集群最多允许 1 个节点故障,5 节点集群最多允许 2 个节点故障。

节点不会动态加入集群:Raft 集群的成员列表是预先配置在RaftNodes中的(替换节点需要手工重新配置并滚动重启,见下文"运维场景")。启动时 orchestrator 依据这份列表建立初始 peer 关系,详见 go/raft/raft.go#L120-L167 中的Setup()。

每个节点拥有独立的专属后端数据库

与 shared-backend 模式不同,raft 模式下每个 orchestrator 节点各自连接一个专属的后端数据库,二者是一一对应的(1:1映射),且无需任何复制关系:

  • MySQL 后端:不需要配置复制,但后端 MySQL 本身有 replica 也无妨。部署建议是将 MySQL 与 orchestrator 放在同一台主机上;
  • SQLite 后端:配置BackendDB: "sqlite"即可,orchestrator 已内置 SQLite,无需安装外部依赖。
"BackendDB": "sqlite", "SQLite3DataFile": "/var/lib/orchestrator/orchestrator.db",

选择哪种后端可参考 docs/configuration-backend.md 与 docs/high-availability.md。实际 CI 环境里orchestrator/raft就以 SQLite 后端运行,见 conf/orchestrator-raft-env.conf.json。

节点间通信非常稀疏

Raft 模式下 orchestrator 节点之间不共享拓扑探测数据——每个节点都独立地发现(probe)全部 MySQL 服务器。节点间只传输 raft 协议本身所需的共识消息,以及 leader 向 follower 分发的用户指令与故障恢复事件(详见下文"行为特征")。这种低通信量设计使orchestrator/raft特别适合高延迟的跨 DC 网络。

配置指南:RaftEnabled 等核心参数详解

在 3 节点部署中,需要在每个节点上配置以下内容:

"RaftEnabled": true, "RaftDataDir": "<path.to.orchestrator.data.directory>", "RaftBind": "<ip.or.fqdn.of.this.orchestrator.node>", "DefaultRaftPort": 10008, "RaftNodes": [ "<ip.or.fqdn.of.orchestrator.node1>", "<ip.or.fqdn.of.orchestrator.node2>", "<ip.or.fqdn.of.orchestrator.node3>" ],

各参数含义(字段定义见 go/config/config.go#L111-L116):

参数作用与注意事项
RaftEnabled必须为true,否则 orchestrator 运行在 shared-backend 模式,所有Raft*配置被忽略
RaftDataDirorchestrator 的数据目录,必须可写;目录不存在时 orchestrator 会尝试自动创建
RaftBind本节点用于 raft 通信的 IP 或完整主机名,该地址也必须出现在RaftNodes中
DefaultRaftPortraft 通信端口,可改为任意端口,但所有节点必须一致;默认值10008
RaftNodes集群全部节点的 IP 或主机名列表,须包含RaftBind所指的本节点

一个可直接参考的完整示例(对应 docs/configuration-raft.md):

"RaftEnabled": true, "RaftDataDir": "/var/lib/orchestrator", "RaftBind": "10.0.0.2", "DefaultRaftPort": 10008, "RaftNodes": [ "10.0.0.1", "10.0.0.2", "10.0.0.3" ],

使用完整主机名同样可行:

"RaftEnabled": true, "RaftDataDir": "/var/lib/orchestrator", "RaftBind": "node-full-hostname-2.here.com", "DefaultRaftPort": 10008, "RaftNodes": [ "node-full-hostname-1.here.com", "node-full-hostname-2.here.com", "node-full-hostname-3.here.com" ],

配置的强制性校验(源码佐证)

配置在启动时会被严格校验,不符合要求 orchestrator 会直接拒绝启动。见 go/config/config.go#L569-L577 与对应的单元测试 go/config/config_test.go#L154-L184:

  • RaftEnabled = true时RaftDataDir必须定义,否则报错RaftDataDir must be defined since raft is enabled (RaftEnabled);
  • RaftEnabled = true时RaftBind必须定义,否则报错RaftBind must be defined since raft is enabled (RaftEnabled);
  • RaftAdvertise未配置时默认取RaftBind的值。

另外,RaftNodes中的条目若未携带端口,会自动补上DefaultRaftPort;若条目为 IP 地址则直接使用,为主机名则会先做 DNS 解析,逻辑见 go/raft/raft.go#L178-L212。

NAT、防火墙与路由:RaftAdvertise 和 HTTPAdvertise

若 raft 节点之间需要经过 NAT 网关通信,需额外设置:

  • "RaftAdvertise": "<ip.or.fqdn.visible.to.other.nodes>"

即其他节点应该访问的本节点地址。否则其他节点会按RaftBind的地址去连接而失败。

此外,raft 节点会把 HTTP 请求反向代理给 leader(见下文),orchestrator 默认按RaftAdvertise启发式推断 leader 的 HTTP 地址。若处于 NAT、端口重映射等环境下无法正确推断,可显式指定:

  • "HTTPAdvertise": "scheme://hostname:port"

例如"HTTPAdvertise": "http://my.public.hostname:3000",表示"假设本节点是 leader 时,外部应通过哪个 HTTP 地址访问它"。

HTTPAdvertise的校验非常严格(go/config/config.go#L591-L607,测试见 go/config/config_test.go#L186-L216):必须带http://或https://协议头、必须包含主机名、必须包含端口号、且不允许带路径。leader 的 HTTP URI 计算逻辑见 go/raft/raft.go#L96-L116:优先使用HTTPAdvertise,否则按RaftAdvertise的主机名 +ListenAddress的端口 +UseSSL决定的协议拼接。

单节点 raft:测试环境的特殊形态

生产环境应使用 3 或 5 节点;测试环境可以运行单节点orchestrator/raft。单节点模式下该节点隐式成为 leader,并向自己广播 raft 消息。配置方式有两种:

  1. 配置空的RaftNodes:
"RaftNodes": [],
  1. 或配置一个与RaftBind(或RaftAdvertise)完全一致的节点:
"RaftEnabled": true, "RaftBind": "127.0.0.1", "DefaultRaftPort": 10008, "RaftNodes": [ "127.0.0.1" ],

源码在 go/raft/raft.go#L140-L144 中检测到"peer 列表为空或只有一个且等于本节点"时清空 peers 并启用单节点模式;对应底层实现在 go/raft/store.go#L81-L85(EnableSingleNode = true,节点自举为 leader)。

流量接入:只有 leader 能执行变更

只有 leader 被允许执行变更操作。因此必须保证客户端流量只到达 leader。官方提供两条路线:

方式一:代理到 leader(/api/leader-check)

最简单的做法是在 orchestrator 服务之上架设 HTTP 代理(如 HAProxy),并使用/api/leader-check作为健康检查:任意时刻至多一个orchestrator 节点对该检查返回HTTP 200/OK,其余节点返回HTTP 404/Not found。只把流量导向通过该检查的节点。

该端点还支持自定义失败状态码:访问/api/leader-check/503会得到503响应(其他数字同理)。实现见 go/http/api.go#L2818-L2829:

func (this *HttpAPI) LeaderCheck(params martini.Params, r render.Render, req *http.Request) { respondStatus, err := strconv.Atoi(params["errorStatusCode"]) if err != nil || respondStatus < 0 { respondStatus = http.StatusNotFound } if logic.IsLeader() { r.JSON(http.StatusOK, "OK") } else { r.JSON(respondStatus, "Not leader") } }

一个可直接使用的 HAProxy 配置示例(来自 docs/raft.md):

listen orchestrator bind 0.0.0.0:80 process 1 bind 0.0.0.0:80 process 2 bind 0.0.0.0:80 process 3 bind 0.0.0.0:80 process 4 mode tcp option httpchk GET /api/leader-check maxconn 20000 balance first retries 1 timeout connect 1000 timeout check 300 timeout server 30s timeout client 30s default-server port 3000 fall 1 inter 1000 rise 1 downinter 1000 on-marked-down shutdown-sessions weight 10 server orchestrator-node-0 orchestrator-node-0.fqdn.com:3000 check server orchestrator-node-1 orchestrator-node-1.fqdn.com:3000 check server orchestrator-node-2 orchestrator-node-2.fqdn.com:3000 check

提示:也可使用orchestrator-client替代代理方案,见下文。

方式二:代理到健康 raft 节点(/api/raft-health)

这是上述约束的放宽版本:健康的 raft 节点会把请求反向代理给 leader。你可以(在 Kubernetes 等场景下尤其推荐)向任意健康成员发送请求,但绝不能访问不健康(即被隔离出 quorum)的成员。

判断节点是否健康使用/api/raft-health:

  • HTTP 200/OK:该节点属于健康 raft 组,可以接收流量;
  • HTTP 500/Internal Server Error:该节点不属于健康组。

需要注意两个短暂窗口:刚启动、leader 尚未选出时,以及leader 重新选举期间,所有节点都可能短暂报告为不健康。实现见 go/http/api.go#L2983-L2993,其底层依赖 go/raft/raft.go#L254-L261 的IsHealthy()(状态为 Leader 或 Follower 即健康)。

反向代理的完整机制在 go/http/raft_reverse_proxy.go#L14-L47:非 leader 节点一旦得知 leader 的 URI,就会用httputil.NewSingleHostReverseProxy把请求转发过去;leader 的 URI 由 leader 通过 raft 日志广播的leader-uri命令发布(go/raft/raft.go#L154-L161、go/logic/command_applier.go#L285-L292)。节点间还通过request-health-report心跳机制互相确认存活(go/raft/raft.go#L352-L377)。

orchestrator-client:无需代理的替代方案

orchestrator-client 是一个包装脚本,通过 HTTP API 访问 orchestrator 服务,提供与orchestrator命令行几乎一致的接口。

关键点:可以把全部 orchestrator API 端点一次性交给orchestrator-client,它会自动判断哪个端点是 leader,并把请求直接发给 leader——这样就完全不需要代理了:

export ORCHESTRATOR_API="https://orchestrator.host1:3000/api https://orchestrator.host2:3000/api https://orchestrator.host3:3000/api"

如果你已经有代理,orchestrator-client也支持只配置代理这一个端点:

export ORCHESTRATOR_API="https://orchestrator.proxy:80/api"

ORCHESTRATOR_API可以统一配置在/etc/profile.d/orchestrator-client.sh中(若该文件存在,会被orchestrator-client自动加载)。常用命令示例:

orchestrator-client -c clusters # 列出所有集群 orchestrator-client -c discover -i 127.0.0.1:22987 # 发现实例 orchestrator-client -c topology -i 127.0.0.1:22987 # 打印拓扑 ASCII 树 orchestrator-client -c relocate -i 127.0.0.1:22988 -d 127.0.0.1:22987 # 移动 replica

行为特征与运行语义:raft 模式下的注意事项

基于 docs/raft.md 并结合源码验证,raft 模式下 orchestrator 有以下关键行为:

  • 每个节点独立执行全部拓扑发现:3 节点部署中,每个 MySQL 拓扑服务器会被 3 个不同的 orchestrator 节点各自访问探测;

  • 各自独立分析:正常情况下各节点看到近乎一致的拓扑视图,但每个节点独立运行自己的 analysis(故障检测分析);

  • 各自写各自的专属后端 DB(MySQL 或 SQLite),数据相互独立;

  • 节点间通信稀疏且不关联事务性数据库提交:leader 只向其他节点分发被拦截的用户指令(如begin-downtime、register-candidate等)以及进行中的故障恢复事件。命令分发表完整可见于 go/logic/command_applier.go#L38-L92,涵盖discover、forget、begin-downtime、end-downtime、register-candidate、ack-recovery、write-recovery、put-key-value、put-instance-tag等 20 余种操作,统一经 raft 日志有序回放(go/raft/fsm.go#L33-L52);

  • 所有用户变更必须经过 leader 的 HTTP API:绝不能直接操作后端数据库,否则该改动不会发布给其他节点,会造成各节点数据不一致;

  • 命令行模式被拒绝:raft 模式下运行orchestrator可执行文件的 CLI 会直接拒绝。源码 go/app/cli.go#L141-L144 会输出提示并退出:

    Orchestrator configured to run raft ("RaftEnabled": true). All access must go through the web API of the active raft node. You may use the orchestrator-client script ... You may override this with --ignore-raft-setup

    也就是说,日常操作应改用 orchestrator-client;--ignore-raft-setup是"我清楚我在做什么"的风险开关,若要使用它,更合理的方式是直连 leader 的后端数据库;

  • 只在服务节点安装 orchestrator 二进制,orchestrator-client脚本可安装在任意需要的地方;

  • 容错能力:3 节点集群最多 1 个节点故障,5 节点集群最多 2 个节点故障,不影响可用性;

  • 节点不能没有后端 DB:SQLite 内嵌于 orchestrator,天然无此问题;MySQL 后端下,若长时间连不上后端 DB,orchestrator 服务会主动退出(bail out);

  • 节点可宕机后恢复:重新加入 raft 组后,会收到离线期间错过的全部事件。无论离线多久,只要本地没有相关 raft 日志/快照,其他节点会自动把最新快照推给它;

  • 无法加入 raft 组则退出:orchestrator 服务若无法加入集群会直接终止。

快照与日志回放(源码级机制)

上述"自动补快照"的能力依赖 raft 的日志存储与快照存储:

  • 每个节点的 raft 日志(log store)和稳定存储(stable store)落在一个 SQLite 文件raft_store.db中(表raft_log、raft_store),实现见 go/raft/rel_store.go#L31-L54;
  • 快照存储为磁盘文件(snapshots/目录),默认保留 10 份(retainSnapshotCount),并带 CRC64 校验,见 go/raft/file_snapshot.go#L22-L28 与 go/raft/raft.go#L42-L47;
  • 快照内容由 go/logic/snapshot_data.go#L33-L56 定义:包括实例键(InstanceKey/MinimalInstance)、停机维护(downtime)记录、候选实例、故障检测、KV 存储、恢复记录、集群别名、访问令牌等,经 gzip + JSON 序列化后随 raft 分发;Restore()会把快照内容整体应用到本地后端 DB,并丢弃快照中不存在的旧实例。

主要优势

  • 高可用:leader 故障自动重选,节点故障不影响整体;
  • 共识防脑裂:故障恢复由处于 quorum 中的 leader 节点执行,不会被隔离节点干扰;
  • 支持嵌入式 SQLite 后端:无需为 orchestrator 准备 MySQL,虽然 MySQL 后端同样支持;
  • 跨节点通信量小:非常适合高延迟的跨 DC 网络。

实战场景:跨数据中心网络隔离(DC fencing)

考虑三个数据中心DC1、DC2、DC3,我们在每个 DC 各部署一个orchestrator/raft节点:

当DC2发生网络隔离时会发生什么?DC2内的节点失去与另外两个节点的联系,凑不齐 quorum,因此 leader 必然留在DC1或DC3(拥有共识的那一侧)。隔离中的节点既不能当选 leader,也不会被客户端选为可用节点;整个隔离期间,MySQL 拓扑管理(包括故障检测与恢复)始终由 quorum 侧的 leader 完成。恢复网络后,DC2节点重新加入 raft 组,通过日志回放或快照补齐离线期间错过的全部变更:

orchestrator/raft的 KV 写入同样走 raft 协议:leader 决定写入后会把请求发布给所有 raft 节点,各节点按自身配置独立应用,形成最终一致(eventual-consistent)的跨 DC 主库发现。详细说明见 docs/kv.md#kv-and-orchestratorraft。

运维场景:节点故障、重新供给与替换

以下场景来自 docs/deployment-raft.md:

  • 节点崩溃:重启节点(如适用先启动 MySQL 服务),再启动 orchestrator 服务,它会加入 raft 组、获取最近快照、追平 raft 日志后恢复正常;
  • 新节点 / 重新供给(后端数据库为空):SQLite 无需任何准备,节点加入后自动获取快照并追平日志;MySQL 后端则需要先为新服务器配置好 orchestrator 数据库权限(详见 docs/configuration-backend.md),此后 orchestrator 可自动加入集群;
  • 克隆后端数据库同样有效(且非必需):可用备份/恢复或 dump/load 方式克隆现有后端(MySQL 逻辑/物理备份,SQLite 用.dump恢复),再启动 orchestrator 服务即可追上日志并加入集群;
  • 替换节点:假设RaftNodes: ["node1", "node2", "node3"],要把node3换成nodeX——先下线node3(node1、node2仍可保持集群运行),准备好nodeX的后端数据,在node1、node2、nodeX上把RaftNodes改为["node1", "node2", "nodeX"];先启动nodeX(此时会因旧成员尚不知晓变更而被拒绝加入),然后依次重启node1、node2,三者即可形成新的健康集群。

另外,raft 节点之间的通信端口是DefaultRaftPort(默认10008):该端口只需对全部 orchestrator 节点开放,其他人无需访问权限。

Roadmap:仍在规划中的能力

根据 docs/raft.md,以下能力仍在开发中:

  • 故障检测需要 quorum 共识:DeadMaster等故障需经多个 orchestrator 节点共同分析确认,才能触发 failover/recovery;
  • 探测共享(与上述互斥):由 leader 把待探测服务器列表按数据中心分配给各节点,使每个 MySQL 服务器只被单个节点探测,降低探测负载,同时让所有 orchestrator 节点看到完全一致的拓扑视图(而非各自独立的视图)。

延伸阅读

  • orchestrator/raft 与同步复制后端对比
  • orchestrator-client 使用指南
  • raft 部署文档
  • raft 相关配置详解
  • 后端数据库配置
  • Key-Value 存储与 orchestrator/raft
  • 高可用方案总览
  • 后端
  • 数据库

【免费下载链接】orchestrator

MySQL replication topology management and HA

项目地址:https://gitcode.com/gh_mirrors/or/orchestrator
点击查看免费下载
上一篇:PHPStan 错误 `mixin.nonObject` 详解:当 `@mixin` 指向了标量类型
下一篇:Inspector MCP 服务器 UX 处理器与交互模式深度解析:Sampling、Elicitation、Roots 与表单生成实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 3:45:43

研发总监如何用AI补设计短板:五大实战场景与工具链

1. 研发总监为什么需要AI补设计短板1.1 一个真实困境&#xff1a;技术强、设计弱&#xff0c;产品就是差口气我带研发团队快十年了&#xff0c;从一线码农做到总监&#xff0c;踩过最大的坑不是技术架构&#xff0c;而是设计。团队里清一色工科背景&#xff0c;后端逻辑写得飞起…

作者头像 李华
网站建设 2026/10/12 3:44:39

【Xilem0.4基础语法学与练】第27课 split 可拖拽分割面板

前言 文档参考&#xff1a;https://docs.rs/xilem/latest/xilem/view/fn.split.html 版本&#xff1a;Xilem 0.4 一、split基础概念 split 是双面板可拖拽分割布局原语&#xff0c;容器只能容纳两个子视图&#xff0c;中间有可拖动分割条&#xff0c;鼠标拖动分割条可以动态修…

作者头像 李华
网站建设 2026/10/12 3:44:23

Spring Boot融合人脸识别,构建智能出勤管理系统

做毕设这些年&#xff0c;见过太多人一上来就选“XX管理系统”&#xff0c;最后交上去的功能千篇一律&#xff1a;增删改查、登录注册、导个Excel就完了。但“基于人脸识别的出勤管理系统”这个题目不一样&#xff0c;它天然带着一个技术亮点——人脸识别&#xff0c;做完之后既…

作者头像 李华