RocketMQ4.9.7性能压测揭秘

问题与挑战

随着公司“降本增效”理念逐步深入落实贯彻,消息中间件与运维团队今年备战“双十一”的基本原则:不增加新的资源投入情况下,确保今年“双十一”平稳进行。

为了应对“双十一”,必须对现有集群的性能进行摸底,故为此搭建了一个4主4从的集群,48C/256G/SSD磁盘,200个主体、400个消费组同时运行,发现集群的总TPS达到28W后集群就出现了Commitlog文件转发延迟,出现拐点,压测结束,压测情况如下图所示:

6744f79f4ee26ca894dab5395fe1c1bc.png

5d7ee8dfb5dcf367ab02994697ddb8a3.png

经过初步压测,4.8.0版本RocketMQ Broker单节点在CPU、内存、磁盘都空闲的情况下单节点TPS才7W,资源浪费实在太严重,无法满足“双十一”的诉求。

通过阅读官方RocketMQ版本的变更日志,我们发现RocketMQ后续版本进行了大量的性能优化,故决定对新版本进行性能压测。

RocketMQ4.9.7压测调优

1

RocketMQ内部转发延迟

我们将压测环境的RocketMQ版本升级到4.9.7,不做任何参数调整的情况下,使用200个主题,400个消费组采用等比例发送256b、512b、1k、4k、1M的消息,TPS可以持续达到23W/TPS,性能提升十分显著,为此通读RocketMQ 4.9.2到4.9.7版本的更新日志,发现官方性能调优的核心要点:

1. 优化锁的粒度,将一些耗时长的代码并且可并行运行的代码从锁中移除,提升并发度。

2.优化Commitlog转发ConsumeQueue的性能,主要的优化手段是将FileChannel调整为MappedByteBuffer。

将压测模式切换到固定大小1K再次压测时发现当TPS达到13W/TPS时又出现了内部转发消息延迟。

RocketMQ4.8.0、4.9.7在写入TPS过大后容易出现内部转发延迟,这在生产实践时是不可接受的,因为会造成消息消费处理不及时,为此简单分析了RocketMQ内部转发机制,如下图所示:

5617db79ddbb540d7b4bb8fc14590af5.png

消息写入到pagecache或FileChannel后,就会异步转发到ConsumeQueue、Index文件,在RocketMQ整个转发过程是由ReputMessageService线程承载,该线程会根据需要转发的offset,解析出一条消息,然后依次将消息转发到消息消费队列(ConsumeQueue)、索引文件,即整个过程是一条消息一条消息串行转发,当海量消息需要转发时该过程会出现性能瓶颈。

ReputMessageService线程转发机制如下图所示:

57c03850945d884c36eccd0d17daa2b3.png

针对这个问题能否进行优化呢?上述过程中的所涉及到的各个子步骤能否进行多线程改造呢?带着这个问题翻看了RocketMQ5.x的更新记录,发现官方已经对其进行了优化,具体优化方案如下图所示:

1518394d993cbc198c204a13ebb4b180.png

主要的核心优化点:

1.发送者向broker发送消息

2.broker写入消息到CommitLog

3.ConcurrentReputMessageService 线程按顺序扫描 CommitLog 消息,并将它们拆分为大约 4MB 的 Batch CommitloLog 消息,然后为每个 Batch CommitLog 分配一个有序编号

4.BatchDispatchRequestService 线程池中的线程会并发检查 CommitLog 消息,检查成功后按照排序号放入集合的相应位置

5.DispatchService 线程按顺序取出选中的 CommitLog 消息,将 CommitLog 消息转换为 ConsumeQueue 消息,并将其写入 ConsumeQueue

于是决定将RocketMQ相关的PR从5.x合并到4.9.7版本,再次压测,在TPS达到20W/TPS时会出现消息转发延迟,相比之前13W左右TPS就出现,情况有了明显改进,但后续经过我们的压测发现RocketMQ单节点的上线应该可以在25W左右,故需要继续进行调优。

2

关闭长轮询

在对ConcurrentReputMessageService进行多线程并发处理后有了显著提升,但还是无法跟上Broker单节点承载的上限,故需要继续对进行优化。

为此在压测期间使用arthas工具分析线程栈,查看当前负载最高的线程的线程栈:

8aee1e8453b784ab3f710cfdd10c1e90.png

结合RocketMQ源码分析,如果开启了长轮询,当把Commitlog文件中的消息转发到ConsumeQueue后会立即通知消息拉取线程,告知已有新的消息达到,从而唤醒消息拉取线程,及时将消息传送到消费者。

但是Dispatch线程实在是太宝贵了,这个会影响整个消息从Commitlog转发到ConsumeQueue,那是否可以关闭长轮询机制呢?

接下来从源码级别深入介绍一下RocketMQ长轮询机制。

长轮询是客户端向服务端拉取消息时服务端第一次未查询到新的消息时触发挂起操作,代码的入口:

83461dce1f780ecb5537f0680ce52faa.png

关键点:未查询到消息,即没有新消息需要消费时。

上述代码的关键要点:

第一阶段:

1.如果开启长轮询(longPollingEnable=true),则pollingTimeoutMills为 20s,取自消息拉取客户端brokerSuspendMaxTimeMillis属性。

2.如果未开启长轮询,则pollingTimeoutMills为1s

3.然后构建 PullRequest对象(包含本次拉取超时时间),通过调用PullRequestHoldService线程的suspendPullRequest方将其放入到中的任务队列,放入队列后,并不会阻塞拉取线程处理其他拉取任务。

第二阶段:PullRequestHoldService线程负责通知

ce9f102ea0022d35110f8b3e9f670a82.png

关键点:如果开启长轮询,PullRequestHoldService线程会间隔5s做一次检查,然后调用checkHoldRequest。

在检测的过程中,如果没有新消息到达Broker并且超过了设置的超时时间后会从队列中拉出来,再投递到拉取线程中进行消息拉取,但这次拉取,就不会再走上面的挂起流程,意味着没有拉到消息,就返回给客户端本次未拉取消息。

上面貌似一看,难道长轮询的检测频率是5s,那岂不是还没有短轮询那么及时,答案当然是否定的,因为当消息发送到Broker后将其转发到消息消费队列后会实时再通知,如下代码所示

deb618f66aa33db2e120a6ccd1cdae37.png

故虽然PullRequestHoldService是每5s检测一下,但只要消息一转发就会通知消息消费拉取线程告知有新消息到达从而唤醒消息消费拉取线程。

从这些可以看出,当由于未拉取到消息而阻塞后,长轮询能更加及时将消息推送给消息消费者,但缺点也非常明显,会浪费 Dispatch 线程的CPU时间,影响转发效率。

综合来看,只有当拉取不到新消息时才会触发长轮询机制,而且消息量越多反而不受影响,故生产环境建议关闭长轮询。

3

Broker CPU使用率过高

在基准压测过程中发现在最高吞吐时broker主节点线程cpu维持在60-70%左右,RocketMQ是基于字节的零拷贝,为什么会有这么大的CPU占用呢?

通过监控broker CPU线程使用情况,发现大约有20-25%的CPU消耗在JSON序列化与反序列化。通过线程追踪发现问题就出现在RocketMQ broker在序列化存储消息时,会将消息头序列化,消息头默认的序列化方式是JSON类型,它通过fastjson的JSON.toJSONString方式实现,Broker接收到的所有消息都需要经过序列化的步骤,对应broker这个操作带来的CPU开销是比较大的。

部分序列化代码:

3251bf90c8225862ad84084a61ddc54e.png

81a3cf8fb5c674eb80e4741c6e4a7e24.png

Broker一共支持两种序列化方式:JSON、ROCKETMQ,相对于JSON,ROCKETMQ使用自定义序列化格式来实现,接下来我们对这两种序列化方式使用同样用例对比压测,得出的结果如下:

● 消息的header使用JSON序列

使用JSON格式序列化消息Header,CPU占整个Broker CPU比率为9%左右,对应消息反序列化CPU使用率为13%

7d37464c01b9b9f85169acfa2777ed91.png

● 使用ROCKETMQ序列化

使用ROCKETMQ格式将消息Header序列化,CPU使用率降为3.2%左右,反序列化CPU使用率为降温7%。CPU使用率整体下降11%左右,同时单Broker 吞吐增加8%左右

5447801b3fda908e510f5ed000b82f2d.png

为什么ROCKMET的序列化方式比JSON高,这得益于ROCKETMQ序列化消息简单高效的实现方式。

首先约定ROCKETMQ存储数据含义和长度,通过组装报文拼接为字节数组,在反序列化时使用同样的解析顺序,依次根据数据长度读取字节,再转换为对应类型的值,序列化数据结构如下:

5972198c09f11fb0d0e724738e05f412.png

这种序列化方式只有在RocketMQ 4.x支持,低于这个版本的客户端会在消费时,会出现消息无法解析的情况

启用ROCKETMQ序列化方式,在RocketMQ broker启动参数加上如下参数:

-Drocketmq.serialize.type=ROCKETMQ

4

触发Broker吞吐瓶颈

在压测后期,发现无论是加大发送线程,还是调整Broker配置(工作线程、内存,序列化方式等),Broker单节点的写入TPS也无法突破25W。持续加大压测线程,会导致客户端的RT值升高,但是节点吞吐没有提升。

基于对RocketMQ broker消息持久化线程模型分析,Broker消息是单线程刷盘之后写入到最新的CommitLog文件,为了保证数据一致性和顺序性,会在刷盘加锁,导致所有消息写入时都需要等待锁释放,消息投递、转换、以及分发都是并发处理,但是写CommitLog是单线程,这导致消息Broker写入TPS受限于CommitLog单条消息写入性能。

消息持久化线程模型:

6bced85393a841839c7f2dfac4182875.png

在RocketMQ-4.9.7对刷盘逻辑进行优化,减少锁的范围,但是瓶颈点依然是单线程获取锁。在压测过程中,监听锁内代码执行平均时间为:0.004-0.005ms之间,可以估算出理想情况下,单broker的最大tps为:1s/0.005至 1s/0.006ms,即tps:20w-25w,此TPS在当前压测场景即为单broker最大吞吐的上限

未来展望

降本增效与中间件稳定性治理是中间件领域两个重要的议题。RocketMQ4.9.7目前已经将单线程内存写入模式发挥到了极致,单节点写入TPS可以在20-25W,内存写入的速度已经到了瓶颈,后续的性能突破点主要是放在多Commitlog目录并行写入。

另外,为了降低磁盘存储的高额成本,平台将借鉴目前大厂在云原生领域的探索成果,尝试考虑引入S3分级存储机制,为进一步提升资源的利用效率,降低使用成本而不懈努力。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Multimedia Builder 4.9.7(简称MMB)是一款用于创建多媒体演示、电子书、交互式应用程序等的软件开发工具。它是一个功能强大且易于使用的可视化开发环境,能够让用户轻松地创建专业水平的多媒体作品。 MMB提供了丰富的多媒体制作工具和组件,如图像处理、音频和视频播放、动画效果、按钮和表单设计等。用户可以通过拖拽和释放操作,快速构建各种界面元素,并利用内置的编辑器来编辑和调整素材内容。 与其它类似的软件相比,MMB具有许多独特的功能和优点。首先,它支持多种多媒体格式的导入和使用,包括图片、声音、视频等,使用户可以在项目中自由组合使用各种媒体资源。其次,MMB提供了强大的交互功能,用户可以轻松地添加按钮、链接和动作脚本,实现用户与作品之间的互动。此外,MMB还具备内置的测试和调试功能,帮助用户快速发现和解决问题。 通过MMB,用户无需编写复杂的代码,就能创建出具有多媒体效果和交互性的应用程序。它适用于各种用途,如电子教育、产品演示、广告宣传等。无论是初学者还是有经验的开发者,都可以从MMB的简单操作和丰富功能中受益。 总之,Multimedia Builder 4.9.7是一个功能强大且易于使用的多媒体开发工具,帮助用户创建出专业水平的多媒体作品。无论是学习、工作还是娱乐,MMB都是一个不错的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值