RocketMQ5.0用新开发的 DLedger Controller 模式增强了以前的 DLedger 模式,下面就来从设计聊一下 DLedger Controller 模式是如何来实现Broker的主备自动切换。我们从以下几个方面来分析:
Tips: RocketMQ 源码版本5.0.0
相比之前的高可用架构,增加了一个DLedger Controller用来实现Broker主备的自主切换和高可用。RocketMQ5.0的主备模式解决RocketMQ4.x哪些问题:
在RocketMQ5.0版本新增了DLedger Controller模式来解决上面对的痛点。
DLedger Controller的部署模式有两种:
独立部署
单独部署DLedger Controller集群
内嵌NameServer部署(推荐)
RocketMQ5.0在默认情况下是不会开启内嵌Controller的,通过设置配置:enableControllerInNamesrv=true
DLedger Controller主要用于Broker的选主,而数据的存储能力还是依靠RocketMQ Broker。 这里可以很好使用到MQ原生的存储能力便于维护。
Broker的选主发起主要有三种情况:
Broker 组第一次上线时,调用该方法也没有 Master,此时会让调用的 broker 试着成为 Master,形成 ElectMasterEvent 事件日志并提交提案,副本组中第一个成功应用 ElectMasterEvent 事件日志的 Broker 会成为 Master,并形成只有自己的 SyncStateSet 列表。
选主Broker注册到 DLedger Controller 的元数据:
这些元数据主要用来选主判断。
ELectMaster 主要是在某 Broker 副本组的 Master 下线或不可访问时,重新从 SyncStateSet 列表里面选出一个新的 Master,该事件由 Controller 自身发起。 这里主要是利用了心跳机制, Broker 定期会和 Controller 上报心跳, Controller 也会定期扫描超时的 Broker (scanNotActiveBroker)。如果某个 Broker 心跳超时, Controller 会判断是否为 Master (BrokerId = 0), 如果是 Master, 则会发起 ElectMaster, 选举新的 Broker Master。 选举 Master 的方式比较简单,我们只需要在该组 Broker 所对应的 SyncStateSet 列表中,挑选一个仍然存活的副本(心跳未超时)出来成为新的 Master 即可,选举出来生成 ElectMaster 事件,通过 DLedger 共识后应用到内存元数据,并将结果通知对应的 Broker 副本组(Broker 本身也会有有轮询机制来获取自身副本组的 Master 信息(getReplicaInfo)做进一步保证,防止通知丢失)。 此外, Controller 增加 enableElectUnCleanMaster 参数,这时如果 SyncStateSet 没有符合要求的副本,可以在当前所有的存活的副本中选,但可能大量消息丢失。
哪些Broker能够参加选主这个是有规则的,同时这里也涉及到了我们要说道的Broker的选主范围的缩容和扩容。
在DLedger Controller中保存了这样的一个集合 SyncStateSet :
SyncStateSet表示⼀个 broker 副本组中跟上 Master 的 Slave 副本数加上 Master 的集合, 主要判断标准是 Master 和 Slave 之间的差距。当 Master 下线时 我们会从 SyncStateSet 列表中选出新的主。Master broker 会定期的检测是否需要扩容和缩容 SyncStateSet, 并通过 api 上报 SyncStateSet 列表信息,DLedger Controller 会根据上述信息维护强一致性的 Broker 副本组的 SyncStateSet 列表。
如果⼀个 Slave 副本追赶上了 Master,Master 需要及时向Controller Alter SyncStateSet 。加⼊SyncStateSet 的条件是 slaveAckOffset >= ConfirmOffset(当前 SyncStateSet 中所有副本的 MaxOffset 的最⼩值)。
Broker新增了配置:
haMaxTimeSlaveNotCatchup
表示slave没有跟上Master的最大时间间隔,若在SyncStateSet中的slave超过该时间间隔会将其从SyncStateSet移除。默认为15000(15s)
HaConnection 中记录 Slave 上⼀次跟上 Master 的时间戳 lastCaughtUpTimeMs,该时间戳含义是:每次Master 向 Slave 发送数据(transferData)时记录⾃⼰当前的 MaxOffset 为 lastMasterMaxOffset 以及当前时间戳 lastTransferTimeMs。
ReadSocketService 接收到 slaveAckOffset 时若 slaveAckOffset >= lastMasterMaxOffset 则将lastCaughtUpTimeMs 更新为 lastTransferTimeMs。
Master 端通过定时任务扫描每一个 HaConnection,如果 (cur_time - connection.lastCaughtUpTimeMs) > haMaxTimeSlaveNotCatchUp,则该 Slave 是 Out-of-sync 的。
如果检测到 Slave out of sync ,master 会立刻向 Controller 上报SyncStateSet,从而 Shrink SyncStateSet。
首先就是启用DLedger Controller模式,部署DLedger Controller 不管是内嵌NameServer还是独立部署都可以。
其次就是Broker开启Broker controller模式(enableControllerMode=true,默认值为false关闭状态),配置allAckInSyncStateSet=true,副本之间的复制为同步复。这样就能实现消息不丢失。
DLedger Controller模式能够在最大限度利用RocketMQ原有的存储能力的基础上提供主备自动切换和消息不丢失的方案。