RocketMQ集群性能调优及运维
系统参数调优
在解压 RocketMQ 安装包后,在 bin 目录中有个 os.sh 的文件,该文件由 RocketMQ 官方推荐系统参数配置。通常这些参数可以满足系统需求,也可以根据情况进行调整。
最大文件数
设置用户的打开的最多文件数:
1 | vim /etc/security/limits.conf |
系统参数设置
系统参数的调整以官方给出的为主,下面对各个参数做个说明。设置时可以直接执行 sh os.sh
完成系统参数设定,也可以编辑 vim /etc/sysctl.conf
文件手动添加如下内容,添加后执行 sysctl -p
让其生效。
1 | vm.overcommit_memory=1 |
参数说明:
参数 | 含义 |
---|---|
overcommit_memory | 是否允许内存的过量分配 overcommit_memory=0 当用户申请内存的时候,内核会去检查是否有这么大的内存空间 overcommit_memory=1 内核始终认为,有足够大的内存空间,直到它用完了为止 overcommit_memory=2 内核禁止任何形式的过量分配内存 |
drop_caches | 写入的时候,内核会清空缓存,腾出内存来,相当于 sync drop_caches=1 会清空页缓存,就是文件 drop_caches=2 会清空 inode 和目录树 drop_caches=3 都清空 |
zone_reclaim_mode | zone_reclaim_mode=0 系统会倾向于从其他节点分配内存 zone_reclaim_mode=1 系统会倾向于从本地节点回收 Cache 内存 |
max_map_count | 定义了一个进程能拥有的最多的内存区域,默认为 65536 |
dirty_background_ratio/dirty_ratio | 当 dirty cache 到了多少的时候,就启动 pdflush 进程,将 dirty cache 写回磁盘 当有 dirty_background_bytes/dirty_bytes 存在的时候,dirty_background_ratio/dirty_ratio 是被自动计算的 |
dirty_writeback_centisecs | pdflush 每隔多久,自动运行一次(单位是百分之一秒) |
page-cluster | 每次 swap in 或者 swap out 操作多少内存页为 2 的指数 page-cluster=0 表示 1 页 page-cluster=1 表示 2 页 page-cluster=2 表示 4 页 page-cluster=3 表示 8 页 |
swappiness | swappiness=0 仅在内存不足的情况下,当剩余空闲内存低于 vm.min_free_kbytes limit 时,使用交换空间 swappiness=1 内核版本 3.5 及以上、Red Hat 内核版本 2.6.32-303 及以上,进行最少量的交换,而不禁用交换 swappiness=10 当系统存在足够内存时,推荐设置为该值以提高性能 swappiness=60 默认值 swappiness=100 内核将积极的使用交换空间 |
集群参数调优
调优建议
对 Broker 的几个属性可能影响到集群性能的稳定性,下面进行特别说明。
1. 开启异步刷盘
除了一些支付类场景、或者 TPS 较低的场景(例如:TPS 在 2000 以下)生产环境建议开启异步刷盘,提高集群吞吐。
1 | flushDiskType=ASYNC_FLUSH |
2. 开启 Slave 读权限
消息占用物理内存的大小通过 accessMessageInMemoryMaxRatio 来配置默认为 40%;如果消费的消息不在内存中,开启 slaveReadEnable 时会从 slave 节点读取;提高 Master 内存利用率。
1 | slaveReadEnable=true |
3. 消费一次拉取消息数量
消费时一次拉取的数量由 broker 和 consumer 客户端共同决定,默认为 32 条。Broker 端参数由 maxTransferCountOnMessageInMemory 设置。consumer 端由 pullBatchSize 设置。Broker 端建议设置大一些,例如 1000,给 consumer 端留有较大的调整空间。
1 | maxTransferCountOnMessageInMemory=1000 |
4. 发送队列等待时间
消息发送到 Broker 端,在队列的等待时间由参数 waitTimeMillsInSendQueue 设置,默认为 200ms。建议设置大一些,例如:1000ms~5000ms。设置过短,发送客户端会引起超时。
1 | waitTimeMillsInSendQueue=1000 |
5. 主从异步复制
为提高集群性能,在生成环境建议设置为主从异步复制,经过压力测试主从同步复制性能过低。
1 | brokerRole=ASYNC_MASTER |
6. 提高集群稳定性
为了提高集群稳定性,对下面三个参数进行特别说明,在后面踩坑案例中也会提到。
关闭堆外内存:
1 | transientStorePoolEnable=false |
关闭文件预热:
1 | warmMapedFileEnable=false |
开启堆内传输:
1 | transferMsgByHeap=true |
集群平滑运维
优雅摘除节点
案例背景
自建机房 4 主 4 从、异步刷盘、主从异步复制。有一天运维同学遗失其中一个 Master 节点所有账户的密码,该节点在集群中运行正常,然不能登陆该节点机器终究存在安全隐患,所以决定摘除该节点。
如何平滑地摘除该节点呢?
直接关机,有部分未同步到从节点的数据会丢失,显然不可行。线上安全的指导思路“先摘除流量”,当没有流量流入流出时,对节点的操作是安全的。
流量摘除
1. 摘除写入流量
我们可以通过关闭 Broker 的写入权限,来摘除该节点的写入流量。RocketMQ 的 broker 节点有 3 种权限设置,brokerPermission=2 表示只写权限,brokerPermission=4 表示只读权限,brokerPermission=6 表示读写权限。通过 updateBrokerConfig 命令将 Broker 设置为只读权限,执行完之后原该 Broker 的写入流量会分配到集群中的其他节点,所以摘除前需要评估集群节点的负载情况。
1 | bin/mqadmin updateBrokerConfig -b x.x.x.x:10911 -n x.x.x.x:9876 -k brokerPermission -v 4 |
将 Broker 设置为只读权限后,观察该节点的流量变化,直到写入流量(InTPS)掉为 0 表示写入流量已摘除。
1 | bin/mqadmin clusterList -n x.x.x.x:9876 |
2. 摘除读出流量
当摘除 Broker 写入流量后,读出消费流量也会逐步降低。可以通过 clusterList 命令中 OutTPS 观察读出流量变化。除此之外,也可以通过 brokerConsumeStats 观察 broker 的积压(Diff)情况,当积压为 0 时,表示消费全部完成。
1 | #Topic #Group #Broker Name #QID #Broker Offset #Consumer Offset #Diff #LastTime |
3. 节点下线
在观察到该 Broker 的所有积压为 0 时,通常该节点可以摘除了。考虑到可能消息回溯到之前某个时间点重新消费,可以过了日志保存日期再下线该节点。如果日志存储为 3 天,那 3 天后再移除该节点。
平滑扩所容
案例背景
需要将线上的集群操作系统从 CentOS 6 全部换成 CenOS 7,具体现象和原因在踩坑记中介绍。集群部署架构为 4 主 4 从,见下图,broker-a 为主节点,broker-a-s 是 broker-a 的从节点。
那需要思考的是如何做到平滑替换?指导思想为“先扩容再缩容”。
集群扩容
申请 8 台相同配置的机器,机器操作系统为 CenOS 7。分别组建主从结构加入到原来的集群中,此时集群中架构为 8 主 8 从,如下图:
broker-a、broker-b、broker-c、broker-d 及其从节点为 CentOS 6。broker-a1、broker-b1、broker-c1、broker-d1 及其从节点为 CentOS 7。8 主均有流量流入流出,至此我们完成了集群的平滑扩容操作。
集群缩容
按照第二部分“优雅摘除节点”操作,分别摘除 broker-a、broker-b、broker-c、broker-d 及其从节点的流量。为了安全,可以在过了日志保存时间(例如:3 天)后再下线。集群中剩下操作系统为 CentOS 7 的 4 主 4 从的架构,如图。至此,完成集群的平滑缩容操作。
注意事项
在扩容中,我们将新申请的 8 台 CentOS 7 节点,命名为 broker-a1、broker-b1、broker-c1、broker-d1 的形式,而不是 broker-e、broker-f、broker-g、broker-h。下面看下这么命名的原因,客户端消费默认采用平均分配算法,假设有四个消费节点。
第一种形式
扩容后排序如下,即新加入的节点 broker-e 会排在原集群的最后。
1 | broker-a,broker-b,broker-c,broker-d,broker-e,broker-f,broker-g,broker-h |
注:当缩容摘除 broker-a、broker-b、broker-c、broker-d 的流量时,会发现 consumer-01、consumer-02 没有不能分到 Broker 节点,造成流量偏移,存在剩余的一半节点无法承载流量压力的隐患。
第二种形式
扩容后的排序如下,即新加入的主节点 broker-a1 紧跟着原来的主节点 broker-a。
1 | broker-a,broker-a1,broker-b,broker-b1,broker-c,broker-c1,broker-d,broker-d1 |
注:当缩容摘除 broker-a、broker-b、broker-c、broker-d 的流量时,各个 consumer 均分配到了新加入的 Broker 节点,没有流量偏移的情况。
集群节点进程神秘消失
现象描述
接到告警和运维反馈,一个 RocketMQ 的节点不见了。此类现象在以前从未发生过,消失肯定有原因,开始查找日志,从集群的 broker.log、stats.log、storeerror.log、store.log、watermark.log 到系统的 message 日志没发现错误日志。集群流量出入在正常水位、CPU 使用率、CPU Load、磁盘 IO、内存、带宽等无明显变化。
原因分析
继续查原因,最终通过 history 查看了历史运维操作。发现运维同学在启动 Broker 时没有在后台启动,而是在当前 session 中直接启动了。
1 | sh bin/mqbroker -c conf/broker-a.conf |
问题即出现在此命令,当 session 过期时 Broker 节点也就退出了。
解决方法
标准化运维操作,对运维的每次操作进行评审,将标准化的操作实现自动化运维就更好了。
正确启动 Broker 方式:
1 | nohup sh bin/mqbroker -c conf/broker-a.conf & |
Master 节点 CPU 莫名飙高
现象描述
RocketMQ 主节点 CPU 频繁飙高后回落,业务发送超时严重,由于两个从节点部署在同一个机器上,从节点还出现了直接挂掉的情况。
主节点 CPU 毛刺截图:
从节点 CPU 毛刺截图:
说明:中间缺失部分为掉线,没有采集到的情况。
系统错误日志一
1 | 2020-03-16T17:56:07.505715+08:00 VECS0xxxx kernel: <IRQ> [<ffffffff81143c31>] ? __alloc_pages_nodemask+0x7e1/0x960 |
系统错误日志二
1 | 30 2020-03-27T10:35:28.769900+08:00 VECSxxxx kernel: INFO: task AliYunDunUpdate:29054 blocked for more than 120 seconds. |
说明:系统日志显示错误“page allocation failure”和“blocked for more than 120 second”错误,日志目录 /var/log/messages。
GC 日志
1 | 2020-03-16T17:49:13.785+0800: 13484510.599: Total time for which application threads were stopped: 0.0072354 seconds, Stopping threads took: 0.0001536 seconds |
说明:GC 日志正常。
Broker 错误日志
1 | 2020-03-16 17:55:15 ERROR BrokerControllerScheduledThread1 - SyncTopicConfig Exception, x.x.x.x:10911 |
说明:通过查看 RocketMQ 的集群和 GC 日志,只能说明但是网络不可用,造成主从同步问题;并未发现 Broker 自身出问题了。
原因分析
系统使用 CentOS 6,内核版本为 2.6。通过摸排并未发现 broker 和 GC 本身的问题,却发现了系统 message 日志有频繁的“page allocation failure”和“blocked for more than 120 second”错误。所以将目光聚焦在系统层面,通过尝试系统参数设置,例如:min_free_kbytes 和 zone_reclaim_mode,然而并不能消除 CPU 毛刺问题。通过与社区朋友的会诊讨论,内核版本 2.6 操作系统内存回收存在 Bug。我们决定更换集群的操作系统。
解决办法
将集群的 CentOS 6 升级到 CentOS 7,内核版本也从 2.6 升级到了 3.10,升级后 CPU 毛刺问题不在乎出现。升级方式采取的方式先扩容后缩容,先把 CentOS 7 的节点加入集群后,再将 CentOS 6 的节点移除,详见前面实战部分“RocketMQ 集群平滑运维”。
1 | Linux version 3.10.0-1062.4.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) ) #1 SMP Fri Oct 18 17:15:30 UTC 2019 |
集群频繁抖动发送超时
现象描述
监控和业务同学反馈发送超时,而且频繁出现。具体现象如下图。
预热现象
说明:上图分别为开启预热时(warmMapedFileEnable=true
)集群的发送 RT 监控、Broker 开启预热设置时的日志。
存传输现象
说明:上图分别为开启堆外内存传输(transferMsgByHeap=fals
e)时的 CPU 抖动截图、系统内存分配不足截图、Broker 日志截图。
原因分析
上面展现的两种显现均会导致集群 CPU 抖动、客户端发送超时,对业务造成影响。
预热设置:在预热文件时会填充 1 个 G 的假值 0 作为占位符,提前分配物理内存,防止消息写入时发生缺页异常。然而往往伴随着磁盘写入耗时过长、CPU 小幅抖动、业务具体表现为发送耗时过长,超时错误增多。关闭预热配置从集群 TPS 摸高情况来看并未有明显的差异,但是从稳定性角度关闭却很有必要。
堆外内存:transferMsgByHeap 设置为 false 时,通过堆外内存传输数据,相比堆内存传输减少了数据拷贝、零字节拷贝、效率更高。但是可能造成堆外内存分配不够,触发系统内存回收和落盘操作,设置为 true 时运行更加平稳。
解决办法
预热 warmMapedFileEnable 默认为 false,保持默认即可。如果开启了,可以通过热更新关闭。
1 | bin/mqadmin updateBrokerConfig -b x.x.x.x:10911 -n x.x.x.x:9876 -k warmMapedFileEnable -v false |
内存传输参数 transferMsgByHeap 默认为 true(即:通过堆内内存传输)保持默认即可。如果关闭了,可以通过热更新开启。
1 | bin/mqadmin updateBrokerConfig -b x.x.x.x:10911 -n x.x.x.x:9876 -k transferMsgByHeap -v true |
用了此属性消费性能下降一半
现象描述
配置均采用 8C16G,RocketMQ 的消费线程 20 个,通过测试消费性能在 1.5 万 tps 左右。通过 tcpdump 显示在消费的机器存在频繁的域名解析过程;10.x.x.185 向 DNS 服务器 100.x.x.136.domain 和 10.x.x.138.domain 请求解析。而 10.x.x.185 这台机器又是消息发送者的机器 IP,测试的发送和消费分别部署在两台机器上。
问题:消费时为何会有消息发送方的 IP 呢?而且该 IP 还不断进行域名解析。
原因分析
通过 dump 线程堆栈,如下图:
代码定位:在消费时有通过 MessageExt.bornHost.getBornHostNameString 获取消费这信息。
1 | public class MessageExt extends Message { |
调用 GetBornHostNameString 获取 HostName 时会根据 IP 反查 DNS 服务器:
1 | InetSocketAddress inetSocketAddress = (InetSocketAddress)this.bornHost; |
解决办法
消费的时候不要使用 MessageExt.bornHost.getBornHostNameString 即可,去掉该属性,配置 8C16G 的机器消费性能在 3 万 TPS,提升了 1 倍。