-
1. 事故场景
-
2.原因
-
3.解决方案
-
4.RocketMQ负载均衡
-
4.1.发送方的负载均衡
-
4.2.订阅方的负载均衡
-
5.总结
1. 事故场景
公司业务系统主要通过MQ实现各流程的串联,主要为了解决业务链路长,通过MQ实现异步调用,但是完全依赖MQ也同步出现各种问题,最主要的问题就在于MQ消息堆积问题。
业务数据量过大后,导致MQ消息不能及时消费,消息堆积量增大之后影响正常的业务流转。
以上次生产事故为例,19号发版之后20号业务流量骤增,上午的流量就达到两百多万,消息堆积也一直维持两百多万堆积,由于以前出现过该堆积量,因此并没有做及时处理,一直到21号上午,消息堆积量已经达到1000万,并且其中由于正常的订单需要影响下游系统的,由于消息堆积下游系统一直没有收到消息,影响了正常的业务,项目组紧急开始修复该问题。
2. 原因
说下生产的配置如下:业务系统共三台实例,若正常消费每台消费能力在200/s。
紧急修复该问题时首先就是查找原因,主要原因在于所有消息积压在一台实例上,其它两台实例消费正常,而RocketMQ的负载均衡并不能根据实例的消费能力进行分发消息,因此造成其中一台实例消费能力很慢就会导致消息的总体积压。
3. 解决方案
针对积压情况,我们解决方案是先增加实例提高总体的消费能力,因此先将一直积压消息的实例关闭,然后增加了三个实例,因此总体是5个实例进行处理,但是经过与阿里RocketMQ技术专家指点,RocketMQ负载均衡是分为8个队列进行消息订阅分发的,因此我们我们又增加了5台实例,总体10个实例进行处理。
原因:
-
我们通过trace插件查看生产的链路耗时,得到其中有一条链路耗时在2s左右,导致一个topic上的消息消费能力变慢。
-
这只是一个问题,因此我们消息积压涉及多个Group,因此通过生产消息ID查询消息轨迹,得到由于消息消费失败,消息重试(我们消息重试次数为3次),导致该消息一直无法被消费成功,该消息也一直处于未消费成功状态,这也是积压的一部分,基于该问题,我们设置消息重试次数为1,失败只要进行一次重试即可。这样大大加快了消费能力。
-
查看代码发现代码中存在抛出异常的问题,整个逻辑中抛出异常,在后续重试时依然会失败,导致消息一直无法被结束,因此去除这种不正常的抛异常代码,使消息能快速被处理。
-
通过trace查看时发现hystrix线程池抛出异常,原因大量消息处理时调用feign导致hystrix线程池溢出,因此增大hystrix最大线程数。
-
增大消费能力后后续继续监控发现MySQL出现执行过久问题,通过查找SQL语句发现是批量更新的相关SQL出现问题,通过与DBA进行分析发现生产我们使用PolarDB,在批量更新时由于需要保证事务性,因此会将整个SQL执行加事务处理,同时由于批量更新SQL涉及分库分表,因此整个SQL加锁过久导致其它SQL等待也引发了连接池溢出。因此调整SQL语句同时增加连接池。
这样所有积压的消息都能快速分配到其它实例进行消费,同时将影响正常业务的MQ进行拆分,单独设置topic进行处理,不被总体的消息影响。
4.RocketMQ负载均衡
RocketMQ的负责均衡并不是我们从服务上理解的负载均衡。首先所有的消息都与topic进行绑定,同时每个topic都分为8个队列(逻辑上的)进行区分,同时RocketMQ从发送方和订阅方分为不同的负载均衡。
4.1. 发送方的负载均衡
生产者生产的消息虽然是与topic进行绑定,但是在topic上区分了8个队列,消息是按照轮询的方式发送到对应的队列中,比如生产的第一条消息会发送到Queue 0,生产的第二条消息会发送到Queue 1,以此类推,因此可以知道RocketMQ的生产者发送的消息与topic的Queue队列进行绑定时并不是随机绑定,按照轮询的方式与topic上的Queue进行绑定。
(图片来自RocketMQ官网)
Broker会将Queue平均分配到订阅方的集群中,进行消息的消费。
4.2. 订阅方的负载均衡
这里要明确一个问题,RocketMQ订阅方的负载均衡并不会按照受每个消费者实例消费能力的影响,因此及时消费者实例消费慢但是对应Queue的消息不会分发到其它消费者实例。
订阅方处理Queue的方案有:
订阅方数量大于Queue的数量,超出Queue数量的机器会处理0个Queue上的消息,如图:
订阅方实例数量等于Queue的数量,每个实例会处理1个Queue上的消息,如图:
订阅方数量小于Queue的数量,则每台机器会处理多个Queue上的消息,如图:
如果一台机器消费处理慢,那么就会导致分配到该机器上的Queue消息不能及时处理,因此会导致整个Queue上的消息都造成堆积。
5. 总结
-
RocketMQ负载均衡不感知消费者的消费能力,因此注意消费者的消费能力很重要。
-
注意代码中的逻辑问题,对于重试依然会失败的逻辑要禁止抛出异常
-
注意链路的消费能力,因此可能会出现连锁反应导致消息积压。
-
及时观察MQ消费,及时预警消息积压问题。
-
持续观察后续问题,因为消费能力增加会挑战系统的稳定性,因此要保证消费能力提升后系统稳定性不能被破坏。
参考资料:
1.RocketMQ官方(help.aliyun.com/document\_d…)