RocketMQ引发的磁盘预警复盘

前言

一款优秀的中间件,参数的缺省值必然是经过反复验证得出的最优解,勿动!

发现问题

某台SaaS服务器磁盘不足发出告警。但是另外台业务量更大、配置相同的SaaS服务器一切正常,于是开始着手排查问题。

排查过程

Step 1

怀疑是日志文件导致的磁盘不足。排查后发现并没有大量的日志文件生成,并且归档的脚本运行正常。

Step 2

对比两台服务器的磁盘使用情况。使用df -h指令后发现,是RocketMQ下的store文件夹竟然有80多G,而另外一台业务量更大的服务器只有50多G。基本就定位到了本次磁盘告警的罪魁祸首了。

Step 3

进入目录store/commitlog/,确认是否真的产生了这么多的消息。找到最新的commitlog文件,把文件名换算成G发现一共也就只有20多G的消息,那么剩下的60多G在哪里呢?

Step 4

使用du -sh *指令后发现,consumequeue文件夹大的离谱。consumequeue下只是存储了commitlog的消息索引不应该有这么大啊。排查了几个topic后发现,所有consumequeue文件夹下的索引文件,大小都为477MB。
在这里插入图片描述

Step 5

翻阅源码,找到consumequeue创建文件大小时的配置参数。

org.apache.rocketmq.store.queue.ConsumeQueueStore#createConsumeQueueByType

在这里插入图片描述
所以mappedFileSizeConsumeQueue这个配置决定了consumeQueue的文件大小,查了下生产的配置,发现了问题。
在这里插入图片描述
mappedFileSizeConsumeQueue这里的单位是字节,换算成MB后问题一目了然。
在这里插入图片描述
为了验证上述结论,通过MQ Dashborad手动创建了新的Topic,然后手动发送了一条新的消息。确认了新创建的ConsumeQueue文件就是477MB。

Step 6

到此,满怀欣喜的以为完成了闭环。然而,这是个开始。
再测试环境用相同的配置文件部署了一套新MQ,重复上述的测试流程发现
在这里插入图片描述
文件大小是4K。用stat指令对比了下两个文件
在这里插入图片描述
在这里插入图片描述

Blocks字段指的是512字节构成的块的个数,IO Block是指文件系统的块的大小一般为4096字节。

Step 7

分析下源码,看下RocketMQ在创建文件时,是否会预占空间。

Broker启动时会创建默认的消费存储处理类,在DefaultMessageStore的构造方法中会创建AllocateMappedFileService

org.apache.rocketmq.broker.BrokerController#initialize

在这里插入图片描述

AllocateMappedFileService可以理解为一个异步线程,主要工作是初始化MappedFile和预热MappedFile,这里有一段比较关键的逻辑。

org.apache.rocketmq.store.AllocateMappedFileService#mmapOperation

在这里插入图片描述
在这里插入图片描述
当开启文件预热的配置时,才会去通过mlock方式预占空间。但是,warmMapedFileEnable默认值为false,并且没有做修改。所以按理来说,不应该会有预占空间这回事才对,477MB还是不合理。

Step 8

到此为止,代码层面已经无法解释了。通过github联系上了RocketMQ作者,请教了这个问题。RocketMQ作者给出的排查方向是硬件和文件系统。于是,对比了一下两台SaaS服务器的硬盘,果真还不一样。
一台高效云盘 200GiB (3400 IOPS)
一台是ESSD云盘 PL0 200GiB (4200 IOPS)

以下为RocketMQ作者给出的解释

每个文件初始化的时候,会设置一个filename,不同的文件系统对这个filename的实现是不一样的,如果我猜测没错的话,因为那个ESSD,它为了提高性能,所以在set filename的时候,它底层把那些block全部给预生成出来,这样可以提高写入的性能。但如果是高效云盘,它可能是懒加载的方式,set filename时,它是虚拟的,等到边写的时候边生成,但是这样的话,效率就会比较低。

总结一下就是两种硬件的底层实现方式一个是饿加载,一个是懒加载。这个分析很合理,令人信服。但是出于技术的严谨性,还是需要再验证一下的。

Step 9

刚好强哥有ESSD云盘的阿里云服务器,就麻烦强哥帮忙测试了一下上述的场景,结果翻车了。在这里插入图片描述
只能把最后的希望寄托到文件系统上了,通过mount查看后发现,两台服务器的文件系统都是ext4类型。

解决方式

问题的排查到此为止,先打上一个TODO吧,后续有想法了会继续排查,如果解决了也会更新博客。最后说一下怎么解决这个问题吧。

  1. 生产的RocketMQ是双主模式,所以先使用指令wipeWritePerm停止其中一台broker的写入,静默后找出消息量较小的topic,进行队列的缩容,减少consumeQueue文件的数量。
  2. 重启RocketMQ,释放文件句柄。这里切记不可修改mappedFileSizeConsumeQueue,该配置生效后若发生修改,可能会造成文件的截断,导致消费的异常。
  3. 最后大概释放了20多G的空间出来,算是解决了磁盘空间的问题。但这个神奇的477MB问题,还会继续研究下去。

写在最后

排查问题期间也怀疑过是不是RocketMQ版本的问题,因为部署的是3x版本,GitHub上也找不到该版本的源码了。从介入问题到解决问题大概花了一周的时间吧,真是睁眼MQ闭眼也MQ的。我甚至怀疑,很多人都遇到了这个问题,不过没有修改mappedFileSizeConsumeQueue的默认值,所以文件大小默认也就不到1MB,就算是预占了磁盘空间,也不会有多大的影响。所以跟这个问题也算是蛮有缘分的吧。最后的最后,我觉得mappedFileSizeConsumeQueue的缺省值其实已经过大了, 实际的空载率已经非常高了,consumequeue文件的头部可能都已经是过期的索引了,所以对于消息量不大的应用来说,单个topic的消息量不超过10万条的,我甚至觉得都可以减少mappedFileSizeConsumeQueue的配置值。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值