记一次 RocketMQ 故障演练过程,同时解开了对 RcoketMQ 高可用的一些误解,RocketMQ 版本 4.x
集群配置:2 个 namesrv,3 个 broker(1 个 master-slave、1 个 master)
| clusterName | brokerName | brokerId | brokerRole | IP |
| :------------- | -------------------- | -------- | ------------ | ------------|
| test1-rocketmq | broker-172.16.73.210 | 0 | ASYNC_MASTER | 172.16.73.210 |
| test1-rocketmq | broker-172.16.73.210 | 1 | SLAVE | 172.16.73.184 |
| qa-rocketmq | broker-172.16.73.211 | 0 | ASYNC_MASTER | 172.16.73.211 |
namesrv 也部署在 172.16.73.210、172.16.73.211 两台机器上。broker、namesrv 都使用的 docker 部署。rocketmq1-qa.yzw.cn、rocketmq2-qa.yzw.cn 解析到 172.16.73.210,rocketmq3-qa.yzw.cn:9876 解析到 172.16.73.211。
172.16.73.210 - broker.conf 部分内容如下:
brokerClusterName=test1-rocketmq
brokerId=0
brokerName=broker-172.16.73.210
namesrvAddr=rocketmq1-qa.yzw.cn:9876;rocketmq2-qa.yzw.cn:9876;rocketmq3-qa.yzw.cn:9876;172.16.73.210:9876;172.16.73.211:9876;
brokerIP1=172.16.73.210
brokerIP2=172.16.73.210
brokerPermission=6
brokerRole=ASYNC_MASTER
flushDiskType=SYNC_FLUSH
listenPort=10911
haListenPort=10912
defaultTopicQueueNums=4
fetchNamesrvAddrByAddressServer=true
autoCreateSubscriptionGroup=true
autoCreateTopicEnable=true
flushCommitLogTimed=false
fileReservedTime=168
sendThreadPoolQueueCapacity=40000
复制代码
72.16.73.184 - broker.conf 部分内容如下:
brokerClusterName=test1-rocketmq
brokerId=1
brokerName=broker-172.16.73.210
namesrvAddr=rocketmq1-qa.yzw.cn:9876;rocketmq2-qa.yzw.cn:9876;rocketmq3-qa.yzw.cn:9876;172.16.73.210:9876;172.16.73.211:9876;
brokerIP1=172.16.73.184
brokerPermission=6
brokerRole=SLAVE
flushDiskType=SYNC_FLUSH
listenPort=10911
haListenPort=10912
defaultTopicQueueNums=4
fetchNamesrvAddrByAddressServer=true
autoCreateSubscriptionGroup=true
autoCreateTopicEnable=true
flushCommitLogTimed=false
fileReservedTime=168
sendThreadPoolQueueCapacity=40000
复制代码
72.16.73.211 - broker.conf 部分内容如下:
brokerClusterName=qa-rocketmq
brokerId=0
brokerName=broker-172.16.73.211
namesrvAddr=rocketmq1-qa.yzw.cn:9876;rocketmq2-qa.yzw.cn:9876;rocketmq3-qa.yzw.cn:9876;172.16.73.210:9876;172.16.73.211:9876;
brokerIP2=172.16.73.211
brokerIP1=172.16.73.211
brokerPermission=6
brokerRole=ASYNC_MASTER
flushDiskType=SYNC_FLUSH
listenPort=10911
haListenPort=10912
defaultTopicQueueNums=4
useEpollNativeSelector=true
fetchNamesrvAddrByAddressServer=true
autoCreateSubscriptionGroup=true
autoCreateTopicEnable=true
flushCommitLogTimed=false
fileReservedTime=168
sendThreadPoolQueueCapacity=40000
复制代码
./mqadmin clusterList -n rocketmq1-qa.yzw.cn:9876
复制代码
集群状态正常,日志正常
卸载数据盘
运维通过控制台卸载 172.16.73.210 的数据盘模拟磁盘故障,172.16.73.211、172.16.73.184 不做任何操作。
查看集群状态
./mqadmin clusterList -n rocketmq1-qa.yzw.cn:9876
复制代码
报错连接异常,因为此时 172.16.73.210 数据盘卸载导致 docker 停止服务已经宕机
测试发送消息
172.16.73.210 无法发送消息,172.16.73.211 能够正常发送消息,此时 172.16.73.210 上的 topic 已经发送消息只能从 slave(172.16.73.184)节点消费历史消息,部分 mq 客户端连接已在报错:rocketmq1-qa.yzw.cn:9876 状态异常无法连接。因为 172.16.73.210 的 namesrv 处于故障中不可用。
手动故障转移
手动修改配置将 172.16.73.184 升级为 Master,修改后的 broker.conf 配置如下
brokerClusterName=test1-rocketmq
#修改brokerId为0
brokerId=0
brokerName=broker-172.16.73.210
namesrvAddr=rocketmq1-qa.yzw.cn:9876;rocketmq2-qa.yzw.cn:9876;rocketmq3-qa.yzw.cn:9876;172.16.73.210:9876;172.16.73.211:9876;
brokerIP1=172.16.73.184
brokerPermission=6
#修改角色为master
brokerRole=ASYNC_MASTER
flushDiskType=SYNC_FLUSH
listenPort=10911
haListenPort=10912
defaultTopicQueueNums=4
fetchNamesrvAddrByAddressServer=true
autoCreateSubscriptionGroup=true
autoCreateTopicEnable=true
flushCommitLogTimed=false
fileReservedTime=168
sendThreadPoolQueueCapacity=40000
复制代码
重启服务
docker restart jifgnsgmagm
复制代码
查看集群状态显示两个 master 节点看起来正常,但是查看 172.16.73.184 的 broker.log 日志有很多 WARN 日志:
2024-06-17 13:53:45 WARN PullMessageThread_87 - PULL_OFFSET_MOVED:correction offset. topic=auac_profile_notify_topic, groupId=yzw-user-jicai-qa, requestOffset=703, newOffset=0, suggestBrokerId=0
复制代码
部分 mq 客户端发送消息任然报错,因为 172.16.73.210 的 namesrv 处理故障状态不可用。172.16.73.211 的 namesrv 正常可用。
恢复磁盘(失败)
由于第 2 步操作失误导致数据丢失且无法恢复
重新部署 Master
挂在新磁盘后重新部署一个 broker(master)和 namesrv,把集群恢复成原来的节点组成。但是由于数据丢失了 172.16.73.210 的数据无法找回等于是一个新 broker。重新部署 master 后日志大量报错:
2024-06-18 09:29:51 ERROR PullMessageThread_77 - the topic jc-contract-esign-push not exist, consumer: 172.16.73.231:57878
复制代码
进一步研究 RocketMQ 高可用
本地故障演练使用的 rocketmq 部署方式是 master-slave,这种方式并不是完全高可用的,只能做到 master 宕机后消费者能从 slave 正常消费消息并且任然可能丢失少部分消息(ASYNC_MASTER 的原因)。master-slave 部署方式无法完成自动故障转移,需要手动运维进行故障转移,修改 slave 的配置重启服务将 slave 升级为 master 提供服务。rocketmq 在 4.5 版本后提供了自动故障转移的 DLedger 部署方式,DLedger 使用了 raft 作为一致性协议(最少需要 3 个节点才具备自动容灾切换能力)。
总结
master-slave 模式无法做到完全高可用,或者严谨点说是没有做到高可用,无法自动故障转移必须人工介入。
手动将 slave 升级为 master 后大量警告日志,怀疑升级后的 broker 不可用,只有另一个 broker 是可用的。
将宕机的 master 重新部署后(数据完全丢失),并且日志大量报错。怀疑重新部署后的 master 不可用。
参考链接
https://rocketmq.apache.org/zh/docs/4.x/bestPractice/02dledger#dledger%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA
https://blog.csdn.net/weixin_42405670/article/details/118072900
https://cloud.tencent.com/developer/article/1521583
https://blog.csdn.net/jiajiren11/article/details/80528406
评论