开发者社区> 超努力的写代码> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Apache RocketMQ 4.9.1 高性能优化之路(上)

简介: Apache RocketMQ 4.9.1 高性能优化之路(上)
+关注继续查看

经过社区的投票,Apache RocketMQ 秋天的第一个版本 4.9.1 如约而至,该版本中最值得关注的是高性能优化这块,针对 Broker 端的性能,特别是对小消息的生产性能进行了针对性优化,和 4.9.0 版本相比,小消息实时生产的 TPS 提升了约 28%。


这一批优化相关的 Pull Request(PR)都挂在 ISSUE2883 下,分为 7 个 PR(A-G),接下来我们来看一下这一批优化的细节,大家也可以到 github 查看代码明细。


A、针对事务消息的优化



在当前的版本中,事务消息已经较为成熟,但压测的时候就会发现,默认的配置下每条消息都会打出一条日志:


log.info("Half offset {} has been committed/rolled back", i);


这肯定会影响性能,压测等大流量场景下甚至会导致灾难性影响。所以这个优化最简单,把这个日志改成 debug 就可以了。


B、消除不必要的锁



在 RocketMQ 内部,主从复制和同步刷盘都是多线程协作处理的。以主从复制为例(GroupTransferService),消息处理线程(多个)不断接收消息,产生待复制的消息,另外有一个 ServiceThread 单线程处理复制结果,可以把前者看做数据生产者,后者看做数据消费者,RocketMQ 使用了双 Buffer 来达到批量处理的目的。如下图,消费者正在处理数据的同时,生产者可以不受影响的继续添加数据,第一阶段生产者 Buffer 有 3 条数据,消费者 Buffer 有 2 条数据,由于消费者是单线程,没有别的线程跟它竞争,所以它可以批量处理这 2 条数据,完成后它会交换这两个 Buffer 的引用,于是接下来的第二阶段它又可以批量处理 3 条数据。


image.png



之前 RocketMQ 在生产者写入、交换 Buffer 引用、以及内部处理中都使用了多个重量级锁保证线程安全。但实际上只需要在生产线程写入以及交换 Buffer 引用的时候加轻量级自旋锁就可以,由于这两个操作都是非常快的,因此可以认为每次加解锁都只有 2 次 CAS 操作的开销。


除此之外,WaitNotifyObject 类也进行了优化,减少了需要进入同步代码块的次数。


image.png


RocketMQ 使用 mmap 来方法 CommitLog 文件,其中有一个好处就是 io 操作的时候少了一个内存拷贝。但实际上由于工程的复杂性,代码中仍然会存在各种各样的内存拷贝,我们优化的目标就是消除那些本来可以避免的复制。


这一次我们就在主从复制这里找到了一个优化点,有一个 ByteBuffer,要把其中一部分写到 CommitLog 里面去,原来的代码会创建一个 byte [],然后复制一遍,其实只需要传入 ByteBuffer.array() 给后续方法,然后指明要复制的起止位置就可以了。这样优化后我们还节省了这个 byte[] 的创建,原先复制 1G 的 CommitLog 就会有至少 1G 的 byte[] 对象的分配和 gc 开销,这下也省了。这次修改的部分实际上运行在 Slave 中,但在同步复制的场景下,对消息发送的响应时间还是有影响的。


image.png


从 RocketMQ4.X 开始引入了自旋锁并作为默认值,同时将参数 sendMessageThreadPoolNums(出现消息生产的线程数)改为了 1,这样处理每条消息写 CommitLog 的时候可以省下进出重量锁的开销。
不过这个地方单线程处理,任务有点重,处理消息的逻辑并不是往 CommitLog 里面一写(无法并行)就完事的,还有一些 CPU 开销比较大的工作,多线程处理比较好,经过一些实践测试,4 个线程是比较合理的数值,因此这个参数默认值改为 MIN(逻辑处理器数, 4)。


既然有 4 个线程,还用自旋锁可能就不合适了,因为拿不到锁的线程会让 CPU 白白空转。所以 useReentrantLockWhenPutMessage 参数还是改为 true 比较好。


还有个细节,endTransactionThreadPoolNums 这个参数默认设置成了 sendMessageThreadPoolNums 的至少 4 倍,以避免事务消息量特别大的场景下(比如事务消息压测),二阶段处理速度赶不上一阶段处理速度,进而导致严重的问题。


此外,对刷盘相关的参数也进行了调整。默认情况下,RocketMQ 是异步刷盘,但每次处理消息都会触发一个异步的刷盘请求。这次将 flushCommitLogTimed 这个参数改成 true,也就是定时刷盘(默认每 500ms),可以大幅降低对 IO 压力,在主从同步复制的场景下,可靠性也不会降低。


image.png


写 CommitLog 只能单线程操作,写之前要先获取一个锁,这个锁也就是影响 RocketMQ 性能最关键的一个锁。理论上这里只要往 MappedByteBuffer 写一下就好了,但实践往往要比理论复杂得多,因为各种原因,这个锁里面干的事情非常的多。


由于当前代码的复杂性,这个优化是本批次修改里面改动最大的,但它的逻辑其实很简单,就是把锁内干的事情,尽量的放到锁的外面去做,能先准备好的数据就先准备好。它包括了一下改动:


1、将 Buffer 的大部分准备工作(编码工作)放到了锁外,提前做好。


2、将 MessageId 的做成了懒初始化(放到锁外),这个消息 ID 的生成涉及很多编解码和数据复制工作,实际上性能开销相当大。


3、原来锁内用来查位点哈希表的 Key 是个拼接出来的字符串,这次也改到锁外先生成好。


4、顺便补上了之前遗漏的关于 IPv6 的处理。


5、删除了无用的代码。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
RocketMQ高手之路系列之四:RocketMQ之消息发送(一)
本文主要叙述了消息的结构以及在消息发送之前,客户端启动的流程是怎样的,而客户端实例的创建时消息发送的前提。在下一篇文章中,将介绍具体的消息发送流程。
82 0
RocketMQ高手之路系列:开篇
本篇是RocketMQ源码阅读的开篇,主要对RocketMQ的架构以及相关目录结构进行介绍,为RocketMQ的源码阅读做一些前期准备工作。 源码下载 源码结构介绍 RocketMQ架构
94 0
Apache RocketMQ开发者沙龙-北京站
由于疫情沉寂了大半年的RocketMQ线下活动又要和大家见面了,2020年首次RocketMQ城市社区开发者沙龙将于10月24日在北京举行!
1755 0
Apache RocketMQ 核心技术与实战
《架构师成长系列》之 Apache RocketMQ 专场。
2279 0
读懂这篇文章,你的阿里技术面就可以过关了 | Apache RocketMQ 101
在美国的大学课程中,101是所有课程中的第一门,是新生入学后的必修课程。阿里巴巴中间件技术专家刘振东在上周的Apache RocketMQ开发者沙龙北京站的活动上,进行了主题为《ApacheRocketMQ 101》的分享,帮助开发者从0开始学习 Apache RocketMQ,除了一些基础的入门内容外,还有很多是在社区未发表过的个人所感所悟,首次对外分享。
5526 0
Mac和Linux中Apache RocketMQ的安装和使用(亲测有效,不服来战)
一、项目需要用到Apache RocketMQ Apache RocketMQ™ is an open source distributed messaging and streaming data platform. 这是阿里开源的一个消息中间件框架。
3044 0
Apache RocketMQ 深圳沙龙报名开启!
Apache RocketMQ深圳沙龙报名正式开启。12月16日,Apache RocketMQ将在深圳举办线下沙龙!届时,社区里的PMC Member、Committer、深度用户,来自各路的资深专家汇聚一堂,就分布式消息引擎的最佳实践、性能优化、流计算集成、开源大数据生态和 OpenMessaging 规范等,为大家带来一场技术上的饕餮盛宴,促进消息领域与社区的进一步发展。
2548 0
1940
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载