在 RocketMQ 5.0.0 和 5.1.0 版本中,采用BrokerAddress作为Broker在Controller模式下的唯一标识。导致如下情景出现问题:
BrokerAddress留下的记录没办法和重启后的Broker联系起来,比如说ReplicaInfo, SyncStateSet等数据。在Controller侧采用BrokerName:BrokerId作为唯一标识,不再以BrokerAddress作为唯一标识。并且需要对BrokerId进行持久化存储,由于ClusterName和BrokerName都是启动的时候在配置文件中配置好的,所以只需要处理BrokerId的分配和持久化问题。 Broker第一次上线的时候,只有配置文件中配置的ClusterName和BrokerName,以及自身的BrokerAddress。那么我们需要和Controller协商出一个在整个集群生命周期中都唯一确定的标识:BrokerId,该BrokerId从1开始分配。当某一个Broker被选为Master的时候,在向Name Server中重新注册时,将更改为BrokerId为0 (兼容之前逻辑 brokerId为0代表着Broker是Master身份)。
这时候发起一个GetNextBrokerId的请求到Controller,为了拿到当前的下一个待分配的BrokerId(从1开始分配)。
此时Controller接收到请求,然后走DLedger去获取到状态机的NextBrokerId数据。
Controller将NextBrokerId返回给Broker。
Broker拿到NextBrokerId之后,创建一个临时文件.broker.meta.temp,里面记录了NextBrokerId(也就是期望应用的BrokerId),以及自己生成一个RegisterCode(用于之后的身份校验)也持久化到临时文件中。
Broker携带着当前自己的基本数据(ClusterName、BrokerName和BrokerAddress)以及此时期望应用的BrokerId和RegisterCode,发送一个ApplyBrokerId的请求到Controller。
Controller通过DLedger写入该事件,当该事件(日志)被应用到状态机的时候,判断此时是否可以应用该BrokerId(若BrokerId已被分配并且也不是分配给该Broker时则失败)。并且此时会记录下来该BrokerId和RegisterCode之间的关系。
若上一步成功应用了该BrokerId,此时则返回成功给Broker,若失败则返回当前的NextBrokerId。
若上一步成功的应用了该BrokerId,那么此时可以视为Broker侧成功的分配了该BrokerId,那么此时我们也需要彻底将这个BrokerId的信息持久化,那么我们就可以直接原子删除.broker.meta.temp并创建.broker.meta。删除和创建这两步需为原子操作。
经过上述流程,第一次上线的broker和controller成功协商出一个双方都认同的brokeId并持久化保存起来。
之前的步骤已经正确协商出了BrokerId,但是这时候有可能Controller侧保存的BrokerAddress是上次Broker上线的时候的BrokerAddress,所以现在需要更新一下BrokerAddress,发送一个RegisterBrokerToController 请求并带上当前的BrokerAddress。
Controller比对当前该Broker在Controller状态机中保存的BrokerAddress,若和请求中携带的不一致则更新为请求中的BrokerAddress。
Controller侧在更新完BrokerAddress之后可携带着当前该Broker所在的Broker-set的主从信息返回,用于通知Broker进行相应的身份转变。
如果在正常上线流程中出现了各种情况的宕机,则以下流程保证正确的
BrokerId分配
若是正常重启,那么则已经在双方协商出唯一的BrokerId,并且本地也在broker.meta中有该BrokerId的数据,那么就该注册流程不需要进行,直接继续后面的流程即可。即从RegisterBrokerToController处继续上线即可。
如果是上图中的流程失败的话,那么Broker重启后,Controller侧的状态机本身也没有分配任何BrokerId。Broker自身也没有任何数据被保存。因此直接重新按照上述流程从头开始走即可。
若是Controller侧已经认为本次ApplyBrokerId请求不对(请求去分配一个已被分配的BrokerId并且该 RegisterCode不相等),并且此时返回当前的NextBrokerId给Broker,那么此时Broker直接删除.broker.meta.temp文件,接下来回到第2步,重新开始该流程以及后续流程。
上述情况可以出现在ApplyResult丢失、CAS删除并创建broker.meta失败,这俩流程中。 那么重启后,Controller侧是已经认为我们ApplyBrokerId流程是成功的了,而且也已经在状态机中修改了BrokerId的分配数据,那么我们这时候重新直接开始步骤3,也就是发送ApplyBrokerId请求的这一步。
因为我们有.broker.meta.temp文件,可以从中拿到我们之前成功在Controller侧应用的BrokerId和RegisterCode,那么直接发送给Controller,如果Controller中存在该BrokerId并且RegisterCode和请求中的RegisterCode相等,那么视为成功。
当正确上线之后,之后Broker的请求和状态记录都以BrokerId作为唯一标识。心跳等数据的记录都以BrokerId为标识。 同时Controller侧也会记录当前该BrokerId的BrokerAddress,在主从切换等时候用于通知Broker状态变化。
默认持久化ID的文件在~/store/brokerIdentity,也可以设置storePathBrokerIdentity参数来决定存储路径。在自动主备切换模式下,不要随意删除该文件,否则该 Broker 会被当作新 Broker 上线。
4.x 版本升级遵守 5.0 升级文档流程即可。 5.0.0 和 5.1.0 非持久化BrokerId版本升级到 5.1.1 及以上持久化BrokerId版本按照如下流程:
~/DLedgerController下的数据文件。在上述升级Controller流程中,Broker仍可正常运行,但无法切换。
~/store/epochFileCheckpoint和~/store/epochFileCheckpoint.bak。admin命令的getSyncStateSet来观察)建议停机时先停从再停主,上线时先上原先的主再上原先的从,这样可以保证原来的主备关系。 若需要改变升级前后主备关系,则需要停机时保证主、备的CommitLog对齐,否则可能导致数据被截断而丢失。
| 5.1.0 及以下版本 Controller | 5.1.1 及以上版本 Controller | |
|---|---|---|
| 5.1.0 及以下版本 Broker | 正常运行,可切换 | 若已主备确定则可正常运行,不可切换。若broker重新启动则无法上线 |
| 5.1.1 及以上版本 Broker | 无法正常上线 | 正常运行,可切换 |