Contents
1. 背景与演进
Controller 模式 和 DLedger 模式 是 RocketMQ 实现高可用自动主从切换的两种主要方案。下面从原理、架构差异、优缺点和应用场景进行详细对比。
| 项目 | DLedger 模式 | Controller 模式 |
|---|---|---|
| 引入版本 | RocketMQ 4.5+ | RocketMQ 5.0(RIP-44) |
| 核心目标 | 解决传统 Master-Slave 无法自动切换的问题 | 解决 DLedger 的成本、灵活性、维护问题,统一存储复制 |
| 官方推荐 | 已不推荐新部署(逐渐被替代) | 新集群首选 |
传统 Master-Slave(无切换)→ DLedger(4.5 引入自动切换)→ Controller(5.0 优化后的自动切换)。
2. 原理核心区别
DLedger 模式原理
- 本质:把 数据复制链路 直接换成基于 Raft 的 DLedger。
- 使用 OpenMessaging 的 DLedger 库替换原生 CommitLog,变成 DLedgerCommitLog。
- 一组相同 brokerName 的 Broker(通常 3 或 5 节点)组成一个 DLedger Group。
- Raft 协议同时负责两件事:
- 选主(Leader 选举)
- 日志复制(多数派确认)
- 角色透传:Raft 的 Leader → Broker 的 Master;Follower/Candidate → Slave。
- 客户端和 NameServer 仍然看到传统的 Master/Slave 角色。
- 数据一致性由 Raft 的多数派机制保证,切换时不会丢消息。
关键特点:
- 选举和复制高度耦合在存储层。
- 必须遵循 Raft 多数派(3 副本至少 2 个 ACK,5 副本至少 3 个 ACK)。
- 存储格式与原生 CommitLog 不兼容(包含 term、index 等 Raft 元数据)。
Controller 模式原理
- 本质:选举与复制解耦。
- 引入独立的 Controller 组件(可内嵌 NameServer 或独立部署),使用 DLedger/Raft 仅管理元数据(主要是 SyncStateSet)。
- 数据复制仍使用 RocketMQ 原生的 Master-Slave 复制机制(HA 服务),保留 TransientStorePool、零拷贝等原生存储能力。
- 核心概念:
- SyncStateSet:与 Master 保持同步的副本集合(包括 Master 自己)。Master 定期检测并上报(Expand/Shrink)。
- Elect Master:Master 心跳超时后,Controller 从 SyncStateSet 中选举新 Master,并通知所有副本切换角色。
- Epoch + StartOffset:切换后用 epoch 和物理偏移量处理日志截断,保证一致性。
- Broker 启动时向 Controller 注册,由 Controller 动态分配 brokerId 和角色(无需在配置中写死 brokerRole/brokerId)。
- 支持灵活的 ACK 策略(allAckInSyncStateSet、inSyncReplicas、minInSyncReplicas 等)。
关键特点:
- 选举上移到控制面(Controller),数据面仍用原生复制。
- 支持 2 副本(一主一从)即可具备自动切换能力。
- 存储格式与传统 Master-Slave 完全兼容。
3. 架构与关键差异对比
| 对比维度 | DLedger 模式 | Controller 模式 |
|---|---|---|
| 选举机制 | Raft 内置在存储层(DLedgerCommitLog) | 独立 Controller(DLedger 只管元数据) |
| 数据复制 | Raft 多数派复制 | 原生 HA 复制(异步/同步可配置) |
| 最小副本数 | ≥3(才有自动切换能力) | ≥2(一主一从即可切换) |
| ACK 灵活性 | 严格多数派,不可灵活调整 | 高度灵活(可配置全部 ACK 或最少副本数) |
| 存储兼容性 | 不兼容原生 CommitLog | 完全兼容原生存储 |
| 原生存储能力 | 无法复用 TransientPool、零拷贝等 | 完整复用 |
| 组件依赖 | 无额外组件(自包含) | 需要 Controller(可内嵌 NameServer) |
| Controller 故障影响 | 无此概念 | 只影响切换能力,不影响正常收发 |
| 升级路径 | 数据格式不兼容,难原地升级 | 传统 Master-Slave 可带数据原地升级 |
| 维护成本 | 高(存储特性要双份维护) | 低(统一存储实现) |
4. 故障切换流程对比
DLedger 模式
- Leader 宕机 → Raft 自动触发选举。
- 新 Leader 产生 → 角色透传为 Master。
- 日志通过 Raft 保证已提交数据不丢。
Controller 模式
- Master 心跳超时 → Controller 检测到。
- Controller 从 SyncStateSet 中选举新 Master。
- 通知所有副本切换角色(Broker 也会主动轮询获取最新角色)。
- 新 Master 用 Epoch + Offset 处理日志截断,保证一致性。
- 可通过 enableElectUncleanMaster 控制是否允许从不在 SyncStateSet 的落后副本中选举(可能丢消息)。
5.消息可靠性对比
| 对比项 | DLedger 模式 | Controller 模式(allAckInSyncStateSet=true) | 说明 |
|---|---|---|---|
| 确认机制 | Raft 多数派(3副本至少2个,5副本至少3个) | SyncStateSet 全量确认 | DLedger 是多数派,Controller 默认全量(可调) |
| 数据一致性保证 | Raft 协议强保证(已提交日志不丢) | 依赖 SyncStateSet + Epoch 截断机制 | 两者在正确配置下都能做到不丢 |
| 最小可靠副本数 | ≥3 | ≥2 即可 | Controller 更灵活 |
| 切换时丢消息风险 | 几乎为 0(Raft 已提交不丢) | 默认从 SyncStateSet 选主 → 不丢;开启 unclean 才可能丢 | 可控 |
| 性能开销 | 多数派确认,延迟相对固定 | 全量确认时延迟更高;可降级为部分确认 | Controller 可灵活权衡 |
| 存储层保证 | 强(Raft 日志) | 依赖原生存储 + 复制确认 | DLedger 更“硬” |
核心结论:
- 在正确配置下(allAckInSyncStateSet=true + 禁止 unclean 选举),Controller 的消息不丢失保证可以达到与 DLedger 同级。
- DLedger 的可靠性来自 Raft 协议本身的多数派 + 日志提交语义,更“协议级硬保证”。
- Controller 的可靠性来自 应用层的 SyncStateSet 管理 + 复制确认策略,更灵活,但需要运维正确配置参数。
为什么有人觉得 Controller 可靠性“不如” DLedger?
常见误解来源:
- 默认配置不同
Controller 默认 allAckInSyncStateSet=false,只需要 Master 本地持久化成功就返回(类似异步复制),可靠性较弱。
而 DLedger 默认就是多数派确认,天然更强。 - 2 副本场景
Controller 支持 2 副本切换,但 2 副本下如果开启全量确认,实际上就是“主 + 从都确认”,和传统同步双写一样。此时如果从节点也挂了,会拒绝写入(minInSyncReplicas 保护)。DLedger 本身不推荐 2 副本做自动切换。 - 选举 unclean 选项
Controller 提供了 enableElectUncleanMaster(默认 false)。如果打开,可能从落后副本选主导致丢消息。DLedger 没有这个选项,因为它严格按 Raft 选。 - 历史印象
早期社区讨论中,有人认为“把选举和复制解耦后,一致性保证变弱了”,但实际上官方通过 SyncStateSet + Epoch 机制补齐了一致性。
金融级 / 强不丢场景推荐配置(Controller):
enableControllerMode = true
allAckInSyncStateSet = true # 关键:全量确认
minInSyncReplicas = 2 # 或更高
enableElectUncleanMaster = false # 禁止从不在 SyncStateSet 的副本选主
inSyncReplicas = 2 # 根据实际副本数调整
在这种配置下:
- 消息必须被当前 SyncStateSet 全部确认才返回成功。
- 切换只从已同步的副本中选。
- 与 DLedger 的“多数派已提交不丢”在效果上接近。
DLedger 的优势在于:
- 协议层强制多数派,运维心智负担更低(不太容易配错)。
- 已提交日志的持久性和一致性由 Raft 直接保证。
Controller 的优势在于:
- 支持 2 副本高可用(成本更低)。
- ACK 策略可灵活调整(性能 vs 可靠性可协商)。
- 复用原生存储能力,性能通常更好。
- 元数据选举与数据复制解耦,故障隔离更好。
目前官方对新集群的推荐是 Controller 模式,并在文档中明确可以通过 allAckInSyncStateSet 达到不丢消息的目标。如果你对可靠性有极高要求,建议在 Controller 模式下开启全量确认,并做好监控 SyncStateSet 大小和复制延迟。
6. 应用场景推荐
| 场景 | 推荐模式 | 理由 |
|---|---|---|
| 新建集群(5.x) | Controller | 官方推荐,成本低、灵活、维护简单 |
| 需要 2 副本 高可用 | Controller | DLedger 做不到 |
| 对延迟敏感,想灵活控制 ACK | Controller | 可配置 allAckInSyncStateSet 或最少副本数 |
| 已有传统 Master-Slave,想加自动切换 | Controller | 可带数据原地升级 |
| 老集群(4.x)已部署 DLedger | 继续用 DLedger 或迁移 | 迁移需清空数据(格式不兼容) |
| 极简部署,不想引入额外组件 | DLedger(临时) | 自包含,但长期不推荐 |
| 大规模集群、多副本组 | Controller | 元数据管理更清晰,性能更优 |
| 异地多活、复杂拓扑 | Controller | 复制策略更灵活 |
7. 总结与建议
- DLedger 模式 是“把 Raft 直接塞进存储层”的方案,简单直接但代价高(必须 3 副本以上、ACK 不灵活、存储能力受限、维护成本高)。
- Controller 模式 是“把选举能力上移到控制面”的方案,实现了选举与复制的解耦,完美解决了 DLedger 的痛点,同时保留了原生高性能存储,是 RocketMQ 5.x 高可用的标准答案。
当前最佳实践(2024 年后):
- 新集群直接使用 Controller 模式。
- 已有传统 Master-Slave 集群,优先升级到 Controller(可带数据)。
- 已有 DLedger 集群,规划窗口期进行空盘迁移到 Controller。
官方文档参考:
- Controller 部署:https://rocketmq.apache.org/zh/docs/deploymentOperations/03autofailover/
- RIP-44 设计:https://github.com/apache/rocketmq/wiki/RIP-44-Support-DLedger-Controller
- 本文固定链接: http://www.jiagou.cc/1861/
- 转载请注明: 摘星怪 于 架构迷 发表
