线上环境大规模 RocketMQ 集群不停机优雅升级实践,面试字节跳动 Java 工程师该怎么准备
能否直接升级 Broker 端代码,但高版本的 Broker 直接使用低版本的 Broker 存储目录,即直接升级软件,其示例图如下:
核心思想是先停止老版本的 Broker,然后使用新版本启动 Broker,但使用旧的配置文件。
有了思路,接下来就是要验证方案的可行性。
[](
)2.3 方案验证
理论归理论,在生产环境做任何变更之前,必须有充分的测试验证,版本升级重点需要验证兼容性问题。
[](
)2.2.1 服务端版本兼容性验证
搭建一个上述 MQ 集群,其核心要点:
高版本的 Broker 是否能向低版本的 NameServer 注册路由
低版本的 Broker 是否能向高版本的 NameServer 注册路由
通过 rocketmq-console,去创建多个个 topic,看看其路由信息是否正确,经验证,符合预期。
[](
)2.2.2 客户端与服务端兼容性验证
RocketMQ 的客户端 API 其实比较单一,无非就是消息发送、批量发送,消息消费,由于 4.1 版本不支持事务消息,这次升级甚至都无需验证事务消息,验证的要点:
低版本的客户端是否能正常向高版本 Broker 发送消息,消费消息
高版本的客户端是否能向低版本的 Broker 发送消息,消费消息
测试案例来自哪,其实都不需要我们自己写,直接用官方的 Demo 即可,其代码截图如下:
客户端验证在真正实施过程中,其实比服务端之间的验证要复杂的多,由于各个项目组使用的客户端版本不一,甚至有些项目组会使用 c++、Python 等其他非 Java 客户端,如何精确找到该集群中所有客户端的连接信息(客户端版本、语言类型)至关重要。
官方提供的版本,对消费组的连接信息还是支持的比较友好,我们可以通过写脚本,先查询系统中所有的消费组,然后遍历每一个消费组,可以查询这些消费组的 IP 地址、客户端版本、使用的语言等信息,但开源版本对生产者支持的不友好,没有一个可获取所有发送者相关的接口。
获取消费组消费端的连接方式如下图所示:
故我们采取的方式,主要是基于消费组失败客户端类型,本次升级过程中,我也对 RocketMQ 做了一些定制化开发,可方便获取所有发送方的链接信息,后续会已提交 PR 的方式贡献给官方。
[](
)2.2.3 Broker 端存储格式验证
由于没有空闲资源,本次要使用的升级方式是直接升级软件,但新老版本共用存储目录,基于 RocketMQ 的消息存储协议,从 4.0.0 版本之后就一直没有变化,其验证的关键点如下:
4.8.0 版本是否可以直接使用 4.1.0 生成的存储文件(commitlog 等文件)
4.1.0 版本是否可以直接使用 4.8.0 生成的存储文件
为什么需要验证 4.1.0 版本能兼容 4.8.0 呢?因为如果升级失败,需要回滚,如果 4.1.0 版本不能兼容 4.8.0 的话,会让你没有退路,这在架构设计中是绝对不允许的。
经过验证发现,存储文件是相互兼容的。
[](
)2.2.4 测试环境验证
经过上面三步的验证,已经可以进行升级了,但升级之前,还要在测试环境稳定运行一天,可以将测试环境升级成如下架构:
即不同版本的混搭模式,接受测试环境所有应用服务器的验证,如果测试环境运行没有问题,即可在生产环境进行升级。
[](
)2.4 实施方案
有了上面升级方案,并且已经做了充分的验证,是可以在生产环境执行了,在执行之前,需要对理论设计输出可执行可落地的实施方案,实施方案必须要包括回滚操作,并且这个回滚操作一定要比较容易执行,否则你的方案一定是不那么可靠的。
接下来重点阐述一下实施过程中一些关键步骤,整个升级步骤才有滚动升级,即逐台升级。
1、关闭一个 Broker 的写权限
关闭 Broker 写权限,让应用将流量平滑迁移到其他节点,这样可以有效避免在对该机器进行重启时对业务造成的影响。
sh ./mqadmin updateBrokerConfig -b 192.168.x.x:10911 -n 192.168.xx.xx:9876 -k brokerPermission -v 4
2、带 Broker 写入、消费 tps 接近0时,关闭 broker
ps -ef | grep java
kill pid
3、使用新版本启动 Broker
注意,此过程使用的配置文件为老版本的配置,故此时并没有开启写权限,启动并不会对客户端消息写入造成影响。
4、开启写权限
待新版本启动成功后,既可以开启写权限
sh ./mqadmin updateBrokerConfig -b 192.168.xx.xx:10911 -n 192.168.xx.xx:9876 -k brokerPermission -v 6
评论