RocketMQ常见问题分析以及性能优化

所谓天才,只不过是把别人喝咖啡的功夫都用在工作上了。

                                                                                ——鲁迅

大纲

图示

RocketMQ其他功能

1.消息轨迹

    源码目录下也有说明文档:

    rocketmq-all-4.8.0-source-release\docs\cn\msg_trace\user_guide.md

(1)配置

    Broker 端服务器开启配置:traceTopicEnable=true 

图示

    RocketMQ 集群中每一个 Broker 节点均用于存储 Client 端收集并发送过来的消息轨迹数据。因此,对于 RocketMQ 集群中的 Broker 节点数量并无要求和限制。

    对于消息轨迹数据量较大的场景,可以在 RocketMQ 集群中选择其中一个 Broker 节点专用于存储消息轨迹,使得用户普通的消息数据与消息轨迹数据的物理 IO 完全隔离,互不影响。在该模式下,RockeMQ 集群中至少有两个 Broker 节点,其中一个 Broker 节点定义为存储消息轨迹数据的服务端。

(2)保存消息轨迹的Topic定义

    RocketMQ 的消息轨迹特性支持两种存储轨迹数据的方式:

    系统级的 TraceTopic

    在默认情况下,消息轨迹数据是存储于系统级的 TraceTopic 中(其名称为:RMQ_SYS_TRACE_TOPIC,队列个数为 1)。该 Topic 在 Broker 节点启动时,会自动创建出来(如上所叙,需要在 Broker 端的配置文件中将 traceTopicEnable 的开关变量设置为 true)。

    用户自定义的 TraceTopic

    如果用户不准备将消息轨迹的数据存储于系统级的默认 TraceTopic,也可以自己定义并创建用户级的 Topic 来保存轨迹(即为创建普通的 Topic用于保存消息轨迹数据)。自定义的话需要在 Client 客户端的处理的时候自定义的 TraceTopic。具体见案例。一般推荐使用系统及的 TraceTopic。

(3)案例

    代码在 example 目录下,tracemessage 目录下。

    RocketMQ 在消息审核消费时采用对原来接口增加一个开关参数(enableMsgTrace)来实现消息轨迹是否开启;并新增一个自定义参数(customizedTraceTopic)来实现用户存储消息轨迹数据至自己创建的用户级 Topic。

TraceProducer
TracePushConsumer

    对于定义轨迹的主题,需要先创建这个主题才能收到消息,RocketMQ 不会自动创建该主题。

图一
图二
图三
图四

(4)关键属性

图示

(5)源码解读

    代码实现的核心类是 AsyncTraceDispatcher

    org.apache.rocketmq.client.trace.AsyncTraceDispatcher

    记录消息的轨迹主要是集中在消息发送前后、消息消费前后,可以通过 RokcetMQ 的 Hook 机制。通过如下两个接口来定义钩子函数

DefaultMQProducer()

    SendMessageHook

SendMessageHook

ConsumeMessageHook

ConsumeMessageHook

    通过实行上述两个接口,可以实现在消息发送、消息消费前后记录消息轨迹,为了不明显增加消息发送与消息消费的时延,记录消息轨迹最好使用 异步发送模式。

    核心步骤如下:

    1:遍历收集的消息轨迹数据

    2:获取存储消息轨迹的 Topic

    3:对 TraceContext 进行编码,这里是消息轨迹的传输数据。

    4:将编码后的数据发送到 Broker 服务器。

start()

2.权限控制

    在 RocketMQ4.4.0 版本升级中加入了 ACL 权限管控,ACL 是 access control list 的简称,俗称访问控制列表。访问控制,基本上会涉及到用户、资源、 权限、角色等概念。

    用户的概念,即支持用户名、密码。

    资源:需要保护的对象,消息发送涉及的 Topic、消息消费涉及的消费组,应该进行保护,故可以抽象成资源。    

    权限:针对资源,能进行的操作。 角色:RocketMQ 中,只定义两种角色:是否是管理员。

(1)配置

    acl 默认的配置文件名:plain_acl.yml,需要放在${ROCKETMQ_HOME}/store/config 目录下

    需要使用 acl 必须在服务端开启此功能,在 Broker 的配置文件中配置,aclEnable = true 开启此功能

    RocketMQ 的权限控制存储的默认实现是基于 yml 配置文件。用户可以动态修改权限控制定义的属性,而不需重新启动 Broker 服务节点,因为Broker端有文件监听机制,每隔 500ms 监听、处理、加载文件的变更内容。

    如果 ACL 与高可用部署(Master/Slave 架构)同时启用,那么需要在 Broker Master 节点的${ROCKETMQ_HOME}/store/conf/plain_acl.yml 配置文件中 设置全局白名单信息,即为将 Slave 节点的 ip 地址设置至 Master 节点 plain_acl.yml 配置文件的全局白名单中。

图示

(2)案例实战

    在 Broker 的配置文件中配置,aclEnable = true 开启此功能

图示

    代码得加入一个返回 RPCHook 的方法

代码示例

    如果权限受限制:

图示

    加入权限对应用户:

图示
图示

    发送成功。

    很多时候像控制台配置了后会报错。

图示

    把全局的白名单打开。

图示

常见问题

1.MQ百万消息积压处理

    发生了线上故障,几千万条数据在 MQ 里积压很久。是修复 consumer 的问题,让他恢复消费速度,然后等待几个小时消费完毕?这是个解决方案。 不过有时候我们还会进行临时紧急扩容。

    一个消费者一秒是 1000 条,一秒 3 个消费者是 3000 条,一分钟是 18 万条。1000 多万条,所以如果积压了几百万到上千万的数据,即使消费者恢复了,也需要大概 1 小时的时间才能恢复过来。

    一般如果消息不重要的话就在 consume 上直接释放掉。

    如果 topic 的 messageQueue 设置的比较多,比如设置了 20 个,consume 实例只有 4 个,那么每个 consume 实例对应 5 个 messageQueue,这个时候可以申请临时加机器,增加 consume 实例为 20 个,达到快速消费的目的。

    如果 messageQueue 设置的比较少,比如只设置了 4 个,那么这个时候就不能通过加 consume 机器来解决了,这时候就需要修改消费者代码了,不再消费者消费,而是把要消费的消息放到 mq 的另一个 topic 中,这个 topic 设置 20 个 messageQueue,对应 20 个 consume 实例,进行消费。

2.消息幂等

(1)消息重复的场景

   生产者

    重试机制导致的问题,消息成功发送到 MQ 中,但 MQ 因网络原因未能成功返回,导致重试机制重复发送到 MQ 。

   消费端

    手动提交 offset 未完成

消费者成功消费完消息,未返回 consume_commit 时,系统重启|系统宕机,MQ 重新发送消息到同消息组其他消费者机器,导致消息重复。

(2)什么是幂等性

    对于消息接收端的情况,幂等的含义是采用同样的输入多次调用处理函数,得到同样的结果。例如,一个 SQL 操作:update stat_table set count= 10 where id =1

    这个操作多次执行,id 等于 1 的记录中的 count 字段的值都为 10,这个操作就是幂等的,我们不用担心这个操作被重复。

    再来看另外一个 SQL 操作:update stat_table set count= count +1 where id= 1;

    这样的 SQL 操作就不是幂等的,一旦重复,结果就会产生变化。

(3)MVCC

    多版本并发控制,乐观锁的一种实现,在生产者发送消息时进行数据更新时需要带上数据的版本号,消费者去更新时需要去比较持有数据的版本号, 版本号不一致的操作无法成功。例如博客点赞次数自动+1 的接口:

    public boolean addCount(Long id, Long version);

    update blogTable set count= count+1,version=version+1 where id=321 and version=123

    每一个 version 只有一次执行成功的机会,一旦失败了生产者必须重新获取数据的最新版本号再次发起更新。

(4)去重表

    利用数据库表单的特性来实现幂等,常用的一个思路是在表上构建唯一性索引,保证某一类数据一旦执行完毕,后续同样的请求不再重复处理了(利 用一张日志表来记录已经处理成功的消息的 ID,如果新到的消息 ID 已经在日志表中,那么就不再处理这条消息。)

    利用 RocketMQ 的 key 值

    以电商平台为例子,电商平台上的订单 id 就是最适合的 token。当用户下单时,会经历多个环节,比如生成订单,减库存,减优惠券等等。每一个环节执行时都先检测一下该订单 id 是否已经执行过这一步骤,对未执行的请求,执行操作并缓存结果,而对已经执行过的 id,则直接返回之前的执行结果,不做任何操作。这样可以在最大程度上避免操作的重复执行问题,缓存起来的执行结果也能用于事务的控制等。

RocketMQ性能优化

1.JVM层面

(1)STW

监控暂停

     rocketmq-console 这个是官方提供了一个 WEB 项目,可以查看 rocketmq数据和执行一些操作。但是这个监控界面又没有权限控制,并且还有一些消 耗性能的查询操作,如果要提高性能,建议这个可以暂停。

    一般的公司在运维方面会有专门的监控组件,如zabbix 会做统一处理。

    或者是简单的shell命令

    监控的方式有很多,比如简单点的,我们可以写一个shell 脚本,监控执行rocketmq Java进程的存活状态,如果rocketmq crash 了,发送告警。

消除偏向锁

    大家了解,在 JDK1.8 sync 有偏向锁,但是在 RocketMQ 都是多线程的执行,所以竞争比较激烈,建议把偏向锁取消,以免没有必要的开销。

    -XX:-UseBiasedLocking: 禁用偏向锁

(2)垃圾回收

    RocketMQ 推荐使用 G1 垃圾回收器。

    -Xms8g -Xmx8g -Xmn4g:这个就是很关键的一块参数了,也是重点需要调整的,就是默认的堆大小是 8g 内存,新生代是 4g 内存。

    如果是内存比较大,比如有 48g 的内存,所以这里完全可以给他们翻几倍,比如给堆内存 20g,其中新生代给 10g,甚至可以更多些,当然要留一些内存给操作系统来用

    -XX:+UseG1GC -XX:G1HeapRegionSize=16m:这几个参数也是至关重要的,这是选用了G1垃圾回收器来做分代回收,对新生代和老年代都是用G1来回收。这里把G1的region大小设置为了16m,这个因为机器内存比较多,所以region 大小可以调大一些给到16m,不然用2m的region, 会导致region数量过多。

    -XX:G1ReservePercent=25:这个参数是说,在 G1 管理的老年代里预留 25%的空闲内存,保证新生代对象晋升到老年代的时候有足够空间,避免老年代内存都满了,新生代有对象要进入老年代没有充足内存了。默认值是 10%,略微偏少,这里 RocketMQ 给调大了一些。

    -XX:initiatingHeapOccupancyPercent= :30:这个参数是说,当堆内存的使用率达到 30%之后就会自动启动 G1 的并发垃圾回收,开始尝试回收一些垃圾对象。默认值是 45%,这里调低了一些,也就是提高了 GC 的频率,但是避免了垃圾对象过多,一次垃圾回收耗时过长的问题。

    -XX:-OmitStackTraceInFastThrow:这个参数是说,有时候 JVM 会抛弃-些异常堆栈信息,因此这个参数设置之后,就是禁用这个特性,要把完整的异常堆栈信息打印出来。

    -XX:+AIwaysPreTouch:这个参数的意思是我们刚开始指定 JVM 用多少内存,不会真正分配给他,会在实际需要使用的时候再分配给他。

    所以使用这个参数之后,就是强制让 JVM 启动的时候直接分配我们指定的内存,不要等到使用内存的时候再分配。

    -XX:MaxDirectMemorySize=15g:这是说 RocketMQ 里大量用了 NIO 中的 direct buffer,这里限定了 direct buffer 最多申请多少,如果你机器内存比较大,可以适当调大这个值,不了解 direct buffer 是什么,可以自己查看 JVM 三期。

    -XX:-UseLargePages:这个参数的意思是禁用大内存页,某些情况下会导致内存浪费或实例无法启动。默认启动。

2.操作系统层面

(1)基本参数

图示

    vm.overcommit_memory=1

    是否允许内存的过量分配

    当为 0 的时候,当用户申请内存的时候,内核会去检查是否有这么大的内存空间

    当为 1 的时候,内核始终认为,有足够大的内存空间,直到它用完了为止

    当为 2 的时候,内核禁止任何形式的过量分配内存

    vm.swappiness=10

    swappiness=0    仅在内存不足的情况下,当剩余空闲内存低于 vm.min_free_kbytes limit 时,使用交换空间

    swappiness=1    内核版本 3.5 及以上、Red Hat 内核版本 2.6.32-303 及以上,进行最少量的交换,而不禁用交换

    swappiness=10    当系统存在足够内存时,推荐设置为该值以提高性能

    swappiness=60    默认值

    swappiness=100    内核将积极的使用交换空间

    vm.max_max_count=655360

    定义了一个进程能拥有的最多的内存区域,默认为 65536

    ulimit=1000000

    limits.conf 设置用户能打开的最大文件数.

    1、查看当前大小

    ulimit -a

图示

    2、临时修改

    ulimit -n 1000000

图示

    3、永久修改

    vim /etc/security/limits.conf

图示

(2)NIC

        一个请求到 RocketMQ 的应用,一般会经过网卡、内核空间、用户空间。

图示

    网卡

    网络接口控制器(英语:network interface controller,NIC)

    因 Ring Buffer 写满导致丢包的情况很多。当业务流量过大且出现网卡丢包的时候,建议调整 Ring Buffer 的大小,这个大小的设置一定程度上是可以缓解丢包的状况。

(3)Kernel

图示

中断聚合

    在中断(IRQ),

    在操作系统级别,是可以做软中断聚合的优化。

    什么是中断?

    举例,假如你是一位开发同学,和你对口的产品经理一天有 10 个小需求需要让你帮忙来处理。她对你有两种中断方式: 

    第一种:产品经理想到一个需求,就过来找你,和你描述需求细节,然后让你帮忙来改。

    第二种:产品经理想到需求后,不来打扰你,等攒够 5 个来找你一次,你集中处理。

    我们现在不考虑及时性,只考虑你的工作整体效率,你觉得那种方案下你的工作效率会高呢?或者换句话说,你更喜欢哪一种工作状态呢? 很明显,只要你是一个正常的开发,都会觉得第二种方案更好。对人脑来讲,频繁的中断会打乱你的计划,你脑子里刚才刚想到一半技术方案可能也就废了。当产品经理走了以后,你再想捡起来刚被中断之前的工作的时候,很可能得花点时间回忆一会儿才能继续工作。

    对于 CPU 来讲也是一样,CPU 要做一件新的事情之前,要加载该进程的地址空间,load 进程代码,读取进程数据,各级别 cache 要慢慢热身。因此如果能适当降低中断的频率,多攒几个包一起发出中断,对提升 CPU 的工作效率是有帮助的。所以,网卡允许我们对硬中断进行合并。

网卡队列 CPU 绑定

    现在的主流网卡基本上都是支持多队列的,我们可以通过将不同的队列分给不同的CPU核心来处理,从而加快Linux内核处理网络包的速度。这是最为有用的一个优化手段。

    每一个队列都有一个中断号,可以独立向某个CPU核心发起硬中断请求,让CPU来poll包。通过将接收进来的包被放到不同的内存队列里,多个CPU就可以同时分别向不同的队列发起消费了。这个特性叫做 RSS(Receive Side Scaling,接收端扩展)。通过ethtool工具可以查看网卡的队列情况。

关闭 IRQBalance

    IRQBalance 主要功能是可以合理的调配使用各个CPU核心,特别是对于目前主流多核心的CPU,简单的说就是能够把压力均匀的分配到各个CPU核心上,对提升性能有很大的帮助。

    但实际中往往影响 cpu 的使用均衡,建议服务器环境中关闭。

net.core.dev_weight

    每个 CPU 一次 NAPI 中断能够处理网络包数量的最大值,可以根据实际情况调整。

TCP NODEALY

    Nagle算法用于对缓冲区内的一定数量的消息进行自动连接。该处理过程(称为Nagling),通过减少必须发送的封包的数量,提高了网络应用 程序系统的效率。(Nagle虽然解决了小封包问题,但也导致了较高的不可预测的延迟,同时降低了吞吐量。)

    RocketMQ 通讯层已经禁止了

NettyRemotingServer
NettyRemotingClient

缓冲区调整

图示

队列大小调整

图示


我是娆疆_蚩梦,让坚持成为一种习惯,感谢各位大佬的:点赞收藏评论,我们下期见!


上一篇:RocketMQ源码解读之Consumer

下一篇:RocketMQ实战(上)

你可能感兴趣的:(RocketMQ常见问题分析以及性能优化)