Apache RocketMQ 自动主从切换(Controller 模式)快速开始指南:一键部署、状态验证与故障切换实践
【免费下载链接】rocketmqApache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications.项目地址: https://gitcode.com/gh_mirrors/ro/rocketmq
导读
本文基于 Apache RocketMQ 仓库中的 docs/cn/controller/quick_start.md 展开,完整讲解如何快速构建一套具备自动主从切换能力的 RocketMQ 集群:从编译源码、一键启动,到用mqadmin运维命令查看SyncStateSet与BrokerEpoch,再到手动 Kill Master 验证故障切换全过程。读完本文,你将掌握 Controller(DLedger 模式)内嵌 NameServer、内嵌 3 节点集群、独立 3 节点集群三种部署形态的实操方法,并能依据 deploy.md 与 design.md 进一步完成生产级新集群部署与旧集群升级。
架构总览:Controller 让“谁当 Master”不再靠人工
在传统 RocketMQ 主从模式下,Master 与 Slave 的角色由brokerId静态决定,Master 故障时需要人工干预(如修改配置、重启或依赖脚本切换),可用性维护成本高。自动主从切换方案的核心是引入一个独立的Controller组件:
- Controller 负责维护每个 broker 复制组(broker-set)的 SyncStateSet(同步副本集合),并基于多数派(Quorum)协议为集合内副本选举/指派 Master 与 Slave 角色;
- 当 Master 失联后,Controller 会从 SyncStateSet 中自动选出新的 Master,并将角色变更结果通知相关 Broker,实现真正意义上的自动故障转移;
- Controller 本身基于Raft 类协议(DLedger)组成集群,从而自身具备高可用能力,可以独立部署,也可以内嵌在 NameServer 进程中(通过
enableControllerInNamesrv开关开启)。
该架构的详细设计思路见 设计思想,新集群部署与旧集群升级的完整指南见 部署指南。
编译 RocketMQ 源码
在开始部署前,需要先从源码构建发行包:
$ git clone https://github.com/apache/rocketmq.git $ cd rocketmq $ mvn -Prelease-all -DskipTests clean install -U构建成功后,发行包会被输出到distribution/target/目录下,内含bin/(启动脚本与mqadmin运维工具)与conf/(各类配置文件),后续所有操作都在发行包目录内完成。
快速部署:一条命令拉起“1 Controller + 2 Broker”最小集群
一键启动与停止
进入发行包目录后,直接运行快速启动脚本:
#{rocketmq-version} 替换为 rocketmq 实际版本号,例如 5.0.0-SNAPSHOT $ cd distribution/target/rocketmq-{rocketmq-version}/rocketmq-{rocketmq-version}/ $ sh bin/controller/fast-try.sh start需要关闭快速集群时执行:
$ sh bin/controller/fast-try.sh stop从仓库中的脚本实现看,fast-try.sh(distribution/bin/controller/fast-try.sh)实际做了以下事情:
startNameserver:以-Xms512m -Xmx512m的 JVM 参数启动bin/mqnamesrv -c ./conf/controller/quick-start/namesrv.conf(该配置开启了enableControllerInNamesrv = true,即 Controller 内嵌在 NameServer 中);startBroker:以-Xms1g -Xmx1g的 JVM 参数依次启动bin/mqbroker -c ./conf/controller/quick-start/broker-n0.conf和broker-n1.conf两个 Broker;checkConf:启动前会校验三个配置文件是否齐全,缺失即报错退出。
也就是说,快速部署默认会开启1 个内嵌了 Controller 的 NameServer 和 2 个 Broker。相关默认配置位于conf/controller/quick-start/目录(即仓库中的 distribution/conf/controller/quick-start),存储路径默认为/tmp/rmqstore。
默认配置逐项解读
namesrv.conf(distribution/conf/controller/quick-start/namesrv.conf):
enableControllerInNamesrv = true controllerDLegerGroup = group1 controllerDLegerPeers = n0-127.0.0.1:9878 controllerDLegerSelfId = n0| 配置项 | 含义 |
|---|---|
enableControllerInNamesrv | 是否将 Controller 以插件方式内嵌到 NameServer 进程中,true表示开启 |
controllerDLegerGroup | Controller 所在的 DLedger Raft 组名称,组内所有节点必须一致 |
controllerDLegerPeers | Controller 集群全部节点列表,格式为selfId-ip:port,多节点用;分隔 |
controllerDLegerSelfId | 当前节点在 Controller 集群中的自 ID,必须与controllerDLegerPeers中的某个selfId对应 |
broker-n0.conf与broker-n1.conf(broker-n0.conf / broker-n1.conf)配置了两个属于同一 broker-set(brokerName = broker-a)的 Broker 节点,端口与存储路径互不相同:
| 配置项 | broker-n0 | broker-n1 | 含义 |
|---|---|---|---|
brokerName | broker-a | broker-a | 同一复制组内所有副本必须同名 |
brokerId | -1 | -1 | -1表示由 Controller 动态分配 ID |
brokerRole | SLAVE | SLAVE | 启动时均以 SLAVE 身份注册,角色由 Controller 裁决 |
enableControllerMode | true | true | 开启 Broker 的 Controller 模式,接受 Controller 的角色管理 |
controllerAddr | 127.0.0.1:9878 | 127.0.0.1:9878 | Controller 地址(快速部署中为内嵌 Controller 的 NameServer 地址) |
namesrvAddr | 127.0.0.1:9876 | 127.0.0.1:9876 | NameServer 地址 |
allAckInSyncStateSet | true | true | 要求消息在 SyncStateSet 内全部副本确认后才返回成功,是自动切换下保证数据一致性的关键 |
listenPort | 30911 | 30921 | Broker 对外服务端口 |
storePathRootDir/storePathCommitLog | /tmp/rmqstore/node00 | /tmp/rmqstore/node01 | 各自独立的存储路径,避免数据目录冲突 |
提示:
brokerId = -1是 Controller 模式下的典型写法——节点不再自行声明角色,而是由 Controller 在注册与选举流程中动态分配 0(Master)或 1(Slave)等 ID。可对照 ControllerConfig.java 中controllerDLegerGroup、controllerDLegerPeers、controllerDLegerSelfId等字段了解其底层承载。
验证 Controller 集群状态
启动成功后,用运维命令查看 Controller 元数据:
$ sh bin/mqadmin getControllerMetaData -a localhost:9878-a代表集群中任意一个 Controller 的地址。该命令对应仓库中的 GetControllerMetaDataSubCommand.java,会返回 Controller 组的 Leader 信息与全部 Peer 列表。
至此集群启动成功,即可向集群收发消息,并进行下面的切换测试。
查看 SyncStateSet 与 BrokerEpoch
查看 SyncStateSet
SyncStateSet(同步副本集合)记录了当前与 Master 保持同步的副本集合,是 Controller 决策切换的重要依据,可以通过运维工具查看:
$ sh bin/mqadmin getSyncStateSet -a localhost:9878 -b broker-a-a是任意一个 Controller 的地址,-b是目标 broker-set 名称。命令实现在 GetSyncStateSetSubCommand.java。
如果顺利的话,可以看到类似下图的内容(集合内包含当前 Master 与同步中的 Slave 副本信息):
查看 BrokerEpoch
BrokerEpoch 用于标记每一次 Master 选举的代数,防止旧 Master 复活后产生“双主”脑裂,可通过运维工具查看:
$ sh bin/mqadmin getBrokerEpoch -n localhost:9876 -b broker-a-n代表任意一个 NameServer 的地址。命令实现在 GetBrokerEpochSubCommand.java。
如果顺利的话,可以看到类似下图的内容(包含 epoch 代数与 master/slave 的 brokerId 记录):
切换验证:Kill 掉 Master,观察自动故障转移
集群部署成功后,即可手动验证自动主从切换能力。
步骤一:定位并 Kill 原 Master
在上文的快速部署中,两个 Broker 的端口分别为 30911(broker-n0)与 30921(broker-n1),其中被 Controller 选为 Master 的是使用端口30911的进程。通过进程过滤命令找到并杀掉它:
# 查找端口: $ ps -ef|grep java|grep BrokerStartup|grep ./conf/controller/quick-start/broker-n0.conf|grep -v grep|awk '{print $2}' # 杀掉 master: $ kill -9 PID说明:
fast-try.sh的stopBroker实现(见 distribution/bin/controller/fast-try.sh)也使用了类似的ps -ef | grep BrokerStartup | grep <conf> | grep -v grep | awk '{print $2}'模式来按配置定位进程,这里为模拟真实故障直接使用kill -9。
步骤二:观察 Master 是否切换
杀掉 Master 后,再次查看 SyncStateSet:
$ sh bin/mqadmin getSyncStateSet -a localhost:9878 -b broker-a可以发现Master 已经发生了切换——Controller 感知到原 Master 失联(scanNotActiveBrokerInterval默认为 5 秒扫描一次,见 ControllerConfig.java),从 SyncStateSet 中选出新的 Master 并完成角色通知:
整个过程无需人工干预,这即是 Controller 模式相对传统主从模式的核心价值。
Controller 内嵌 NameServer 集群部署(3 节点)
对于生产环境,Controller 需要以多副本集群方式部署以保障自身高可用。第一种形态是Controller 以插件方式内嵌于 NameServer 集群(3 个 Node)。
一键启动
$ sh bin/controller/fast-try-namesrv-plugin.sh start该脚本(fast-try-namesrv-plugin.sh)会依次启动三个 NameServer(内嵌 Controller):
$ nohup bin/mqnamesrv -c ./conf/controller/cluster-3n-namesrv-plugin/namesrv-n0.conf & $ nohup bin/mqnamesrv -c ./conf/controller/cluster-3n-namesrv-plugin/namesrv-n1.conf & $ nohup bin/mqnamesrv -c ./conf/controller/cluster-3n-namesrv-plugin/namesrv-n2.conf &三个节点的配置位于 distribution/conf/controller/cluster-3n-namesrv-plugin,以namesrv-n0.conf为例:
# Namesrv config listenPort = 9876 enableControllerInNamesrv = true # controller config controllerDLegerGroup = group1 controllerDLegerPeers = n0-127.0.0.1:9878;n1-127.0.0.1:9868;n2-127.0.0.1:9858 controllerDLegerSelfId = n0其余两节点(n1、n2)仅listenPort(9886/9896)与controllerDLegerSelfId(n1/n2)不同,controllerDLegerPeers必须三节点完全一致,以组成同一个 DLedger Raft 组。
验证 Controller 集群状态
$ sh bin/mqadmin getControllerMetaData -a localhost:9878-a是任意一个 Controller 的地址。如果 Controller 启动成功,可以看到类似以下内容:
#ControllerGroup group1 #ControllerLeaderId n0 #ControllerLeaderAddress 127.0.0.1:9878 #Peer: n0:127.0.0.1:9878 #Peer: n1:127.0.0.1:9868 #Peer: n2:127.0.0.1:9858接入 Broker 与停止集群
启动成功后,Broker 以 Controller 模式部署(enableControllerMode = true、controllerAddr指向任一 Controller 地址)即可使用该 Controller 集群。
需要快速停止集群时:
$ sh bin/controller/fast-try-namesrv-plugin.sh stop使用fast-try-namesrv-plugin.sh脚本快速部署,默认配置在conf/controller/cluster-3n-namesrv-plugin里面,并且会启动3 个 NameServer 和 3 个 Controller(内嵌于 NameServer)。
Controller 独立集群部署(3 节点)
第二种形态是Controller 独立部署,与 NameServer 完全解耦,适合已有 NameServer 集群、仅需新增 Controller 能力的场景。
一键启动
$ sh bin/controller/fast-try-independent-deployment.sh start该脚本(fast-try-independent-deployment.sh)会启动三个独立 Controller 进程:
$ nohup bin/mqcontroller -c ./conf/controller/cluster-3n-independent/controller-n0.conf & $ nohup bin/mqcontroller -c ./conf/controller/cluster-3n-independent/controller-n1.conf & $ nohup bin/mqcontroller -c ./conf/controller/cluster-3n-independent/controller-n2.conf &三个节点的配置位于 distribution/conf/controller/cluster-3n-independent,以controller-n0.conf为例:
controllerDLegerGroup = group1 controllerDLegerPeers = n0-127.0.0.1:9878;n1-127.0.0.1:9868;n2-127.0.0.1:9858 controllerDLegerSelfId = n0mqcontroller启动入口对应仓库中的 ControllerStartup.java,独立模式不再需要enableControllerInNamesrv与 NameServer 的listenPort,仅需配置 DLedger 组信息。
验证 Controller 集群状态
$ sh bin/mqadmin getControllerMetaData -a localhost:9878如果 Controller 启动成功,可以看到类似以下内容(注意本例中 Leader 为 n1,说明 Leader 由 Raft 选举动态产生,不一定是 n0):
#ControllerGroup group1 #ControllerLeaderId n1 #ControllerLeaderAddress 127.0.0.1:9868 #Peer: n0:127.0.0.1:9878 #Peer: n1:127.0.0.1:9868 #Peer: n2:127.0.0.1:9858接入 Broker 与停止集群
启动成功后,Broker 以 Controller 模式部署即可使用该 Controller 集群。
需要快速停止集群时:
$ sh bin/controller/fast-try-independent-deployment.sh stop使用fast-try-independent-deployment.sh脚本快速部署,默认配置在conf/controller/cluster-3n-independent里面,并且会启动3 个独立部署的 Controller 组成一个集群。
三种部署形态对比与选型建议
| 部署形态 | 启动命令 | 进程数 | 配置文件目录 | 适用场景 |
|---|---|---|---|---|
| 快速部署(内嵌单点) | fast-try.sh start | 1 NameServer(含 Controller)+ 2 Broker | distribution/conf/controller/quick-start | 本地体验、功能验证、开发调试 |
| Controller 内嵌 NameServer 集群 | fast-try-namesrv-plugin.sh start | 3 NameServer(各含 Controller) | distribution/conf/controller/cluster-3n-namesrv-plugin | 希望复用 NameServer 进程、减少部署组件数的生产场景 |
| Controller 独立集群 | fast-try-independent-deployment.sh start | 3 Controller | distribution/conf/controller/cluster-3n-independent | 已有 NameServer 集群、Controller 独立扩缩容的生产场景 |
三种形态下,Broker 侧的接入方式完全一致:设置enableControllerMode = true、controllerAddr = <任一Controller地址>,并将brokerId设为-1交由 Controller 动态分配。
注意事项与最佳实践
- Controller 高可用需要三副本及以上:若需要保证 Controller 具备容错能力,Controller 部署需要三副本及以上(遵循 Raft 的多数派协议,容忍少数节点故障);单副本 Controller 无法在自身故障时继续提供仲裁能力。
- 多机部署时务必修改
controllerDLegerPeers中的 IP:配置参数controllerDLegerPeers中的 IP 地址需要配置成其他节点能够访问的 IP,在多机器部署的时候尤为重要。仓库提供的示例(127.0.0.1)仅供单机快速体验参考,实际部署需根据环境修改调整。 allAckInSyncStateSet与数据一致性:快速部署配置中开启了allAckInSyncStateSet=true,要求消息被 SyncStateSet 内全部副本确认后才返回成功,这是保证 Master 切换后不丢消息的关键设置;生产环境可根据容灾与延迟要求权衡。- 关注不可用 Master 的选举策略:从 ControllerConfig.java 的源码可以看出,Controller 还支持
enableElectUncleanMaster(是否允许选举不在 SyncStateSet 中的 Master,默认false)、electMasterMaxRetryCount(选举失败最大重试次数,默认 3)等高级参数,生产调优时可结合 设计思想 与 部署指南 综合评估。
总结
本文从零完成了 RocketMQ 自动主从切换集群的快速构建与验证:通过fast-try.sh一条命令拉起最小集群,用getControllerMetaData、getSyncStateSet、getBrokerEpoch三个运维命令确认 Controller 与复制组状态,并通过 Kill Master 实测了自动切换能力;随后给出内嵌 NameServer 与独立部署两种 3 节点生产形态的部署方法。基于这套快速开始流程,配合仓库中的 deploy.md(新集群部署与旧集群升级)与 design.md(架构与选举设计),即可将自动主从切换方案落地到实际生产环境。
【免费下载链接】rocketmqApache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications.项目地址: https://gitcode.com/gh_mirrors/ro/rocketmq
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考