实战案例

RocketMQ集群线上故障缩容实战案例

背景

现有一套RocketMQ的四主集群,其中1台机器经常发生消息积压,其他3台相同配置的机器不会有积压。增加机器和消费者的处理方式,效果改善并不明显。进一步查看机器性能指标发现I/O等待居高不下,通过和IT部门沟通并最终确认,该机器存在硬件问题,是电池故障导致的,因此需要停机更换机器电池。

因为该集群属于核心系统,在正式线上操作之前需要在本地预先演练,同时为了让一线用户无感知,我们避开业务高峰期,定于夜间低峰期执行。(理论上可以在任何时间操作,但为了规避风险,选择业务低峰期操作。)

现状分析

线上RocketMQ集群采取四主的部署架构,并部署两台NameSrv,其具体信息如表11-52所示。

image 2025 02 06 23 19 56 829
Figure 1. 表11-52 线上机器列表

本次发生故障的机器IP为10.x.x.16。

操作步骤

(1)关闭目标Broker写权限

bin/mqadmin updateBrokerConfig -b 10.x.x.16:10935 -n
'10.x.x.16:9876;10.x.x.111:9876' -k brokerPermission
 -v 4

出现update broker config success提示,表示10.x.x.16:10935指令执行成功。 这里需要注意如下内容。

1)将Broker的写权限关闭后,非顺序消息不会立即拒绝,而是等客户端路由信息更新后,不会再向该Broker发送消息。 2)updateBrokerConfig指令是针对broker.properties做在线修改,会在修改Broker内存参数的同时将最新的参数值同步写入broker.properties配置文件。 3)brokerPermission的可选值为2(只写权限)、4(只读权限)、6(读写权限)。我们在处理完毕后一定要记得开放写权限,否则会降低集群的生产消费能力。因为Broker关闭写权限之后,其对应的消费者并不会触发重平衡,导致关联到了只读Broker的这一部分消费者永远不能消费集群中的消息。

(2)节点下线

通过rocketmq-console或者执行mqadmin命令监测节点流量。观察InTPS和OutTPS流量,理想情况都为0且不再变化时,该节点就可以写下线了。

在rocketmq-console的查询界面如图11-51所示。

image 2025 02 06 23 22 43 423
Figure 2. 图11-51 查询界面

也可通过如下命令查询节点流量。

bin/mqadmin clusterList -n '10.x.x.16:9876;10.x.x.111:9876'

运维人员在关闭了Broker写权限之后等了半个小时,发现依然有100个左右的生产和消费TPS,导致他不敢继续操作,此时我们查看Broker全部队列情况,发现这个异常TPS来自重试队列。

我们可以通过如下操作判断流量来自哪些topic,效果如图11-52所示。

bin/mqadmin brokerConsumeStats -b 10.x.x.16:10935 -n
'10.x.x.16:9876;10.x.x.111:9876' >> broker16ConsumeS
tats.tmp_1//可隔几秒多拉取几次做对比
image 2025 02 06 23 23 50 751
Figure 3. 图11-52 diff消息延迟已经为0,没有消息生产和消费

通过上面的命令主要是查看#LastTime和#Diff。发现%RETRY%重试类队列#Diff有部分数据,而其他topic均为0。LastTime也储存在%RETRY%队列中,这个数据证明了此Broker的确没有接收新的业务消息,也没有有效的业务消费了,此时可以让该节点下线操作。

(3)停止Broker

使用如下命令停止Broker。

ps -ef | grep broker
Kill pid

注意,这里不能使用kill -9命令强制关闭Broker进程。

观察console,发现节点已经成功下线,如图11-53所示,broker-2已经下线,目前只有3个broker实例在运行。

image 2025 02 06 23 24 45 187
Figure 4. 图11-53 节点成功下线

(4)关机换电池,验证硬盘 经运维人员确认,与同集群内另一台Broker所在的机器10.x.x.111做硬盘I/O效率对比,发现10.x.x.16写入效率较之前有了很大提升,甚至超越了其他Broker实例的机器,确认硬件问题已经解决,如图11-54所示,查看机器IOPS的读写效率,有大量数据读写,而且读写效率很高。 (5)启动NameSrv,启动Broker 执行如下命令启动NameSrv,启动Broker。

nohup bin/mqbroker -c /conf/broker-2.properties &

(6)开启读写权限

image 2025 02 06 23 25 38 500
Figure 5. 图11-54 查看机器IOPS的读写效率

执行如下命令开启读写权限。

bin/mqadmin updateBrokerConfig -b 10.x.x.16:10935 -n
'10.x.x.16:9876;10.x.x.111:9876' -k brokerPermission
 -v 6

观察各节点流量是否恢复正常,如图11-55所示,broker-2生产和消费TPS在4000左右,Broker集群也恢复为4个实例。

image 2025 02 06 23 27 44 613
Figure 6. 图11-55 Broker集群恢复为4个实例

此时通过控制台发现broker-2重新加入集群,并且因为写权限重新开放,生产消费的TPS也逐渐恢复了。通过观察应用日志,我们发现在关闭NameSrv以及Broker时偶尔会报连接错误,后续逐步恢复正常。

RocketMQ在线扩容实战

背景

随着业务发展壮大,我们必然会面临一个问题,现有的RocketMQ服务集群性能不足以支撑当前的业务需求,急需扩容升级。现有的MQ服务投产使用中不能间断,这个时候在线(热)扩容就呼之欲出了,下面为大家分享一次在线扩容的实践。

以快递业务为例,每一个快件在运输的过程中都会经过很多中心、网点,也对应了很多状态的变化,每一次状态变化都会产生大量的操作数据,这些数据又是诸多业务系统必须输入的,因此我们需要一个前置系统接收所有数据的服务,再把数据分发给各业务系统。使用RocketMQ集群承担这些大数据量的接收任务,现有的集群结构(四主四从)如图11-56所示。

image 2025 02 06 23 29 32 560
Figure 7. 图11-56 当前线上环境四主四从逻辑部署图

很快现有的四主四从集群就不能支撑了,此处补充性能不足的证据,扩容形式初步有两套方案,扩容到八主或者八主八从,最终决定了八主八从的方案,考量因素如下。

1)八主八从可以满足业务增长需求。 2)二主、四主四从扩容到八主八从可以实现动态无感知扩容,无须下线从节点,保证了线上应用的正常运行。 3)八主八从方案相比八主方案,当主节点宕机时从节点可以继续支撑消费,更好地保障了消息的实时性,部分业务也确实有这个需求。

扩容操作的集群架构演变如图11-57所示。

总体思路

1)搭建扩容的四主四从RocketMQ实例。 2)更改配置与之前节点保持一致。

image 2025 02 06 23 31 40 995
Figure 8. 图11-57 线上的四主四从扩容为八主八从

3)从现有的Broker上复制${ROCKETMQ_HOME}/store/config/目录下的topics.json和subscriptionGroup.json文件到新的Broker服务器上。 4)启动扩容后的节点加入现有生产集群。

执行步骤

第一步:通知所有生产者和消费者应用负责人实时监控应用,及时发现和上报任何异常。 第二步:新加4台新服务器。 第三步:新加4台新服务器,Linux内核参数调优,调优脚本如下所示。

# 修改 /etc/sysctl.conf 文件
echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
echo 'vm.min_free_kbytes=5000000' >> /etc/sysctl.conf
echo 'vm.drop_caches=1' >> /etc/sysctl.conf
echo 'vm.zone_reclaim_mode=0' >> /etc/sysctl.conf
echo 'vm.max_map_count=655360' >> /etc/sysctl.conf
echo 'vm.dirty_background_ratio=50' >> /etc/sysctl.conf
echo 'vm.dirty_ratio=50' >> /etc/sysctl.conf
echo 'vm.page-cluster=3' >> /etc/sysctl.conf
echo 'vm.dirty_writeback_centisecs=360000' >> /etc/sysctl.conf
echo 'vm.swappiness=10' >> /etc/sysctl.conf

# 应用 sysctl 配置
sysctl -p

# 修改 /etc/profile 文件
echo "ulimit -n 655350" >> /etc/profile

# 修改 /etc/security/limits.conf 文件
echo "$USER hard nofile 655350" >> /etc/security/limits.conf

# 修改 I/O 调度器
echo 'deadline' > /sys/block/sda/queue/scheduler

# 检查 sysctl 配置
sysctl vm.overcommit_memory
sysctl vm.min_free_kbytes
sysctl vm.drop_caches
sysctl vm.zone_reclaim_mode
sysctl vm.max_map_count
sysctl vm.dirty_background_ratio
sysctl vm.dirty_ratio
sysctl vm.page-cluster
sysctl vm.dirty_writeback_centisecs
sysctl vm.swappiness

# 检查 ulimit 设置
su - root -c 'ulimit -n'

# 检查当前 I/O 调度器
cat /sys/block/sda/queue/scheduler

第四步:从生产者现有Broker上复制 ${ROCKETMQ_HOME}/store/config/ 目录下的topics.json和subscriptionGroup.json文件到新Broker服务器上,以保证主题和消费组在新的实例内同步,注意扩容期间不进行主题和消费组的创建(线上环境是关闭自动创建主题和消费组的)。 第五步:启动4台新服务器并加入生产集群。 第六步:通知生产者和消费者在控制台观察消息是否分摊到新Broker上。

至此,在线扩容完成,需要注意评估好对业务的影响,做好通知工作以及兜底方案,确保在扩容过程中出现任何异常都能不影响当前业务的正常运转。