RocketMQ(十二)消息堆积与消费延迟
产生背景
消息处理流程中,如果Consumer的消费速度跟不上Producer的发送速度,MQ中未处理的消息会越来越多,这部分消息就被称为 堆积消息。消息出现堆积会导致消费延迟,以下场景需要重点关注消息堆积和消息延迟的问题。
- 业务系统上下游能力不匹配造成的持续堆积,且无法自行恢复
- 业务系统对消息的消费实时性要求较高,即使是短暂的堆积造成的消费延迟也无法接受。
Consumer使用长轮询Pull模式消费消息时,分为以下两个阶段:
消息拉取
Consumer通过长轮询Pull模式批量拉取的方式从服务端获取消息,将拉取到的消息缓存到本地缓冲队列中。对于拉取式消费,在内网环境下会有很高的吞吐量,所以这一阶段一般不会成为消息堆积的瓶颈。
一个单线程单分区的低规格主机(Consumer,4C8G),其可达到几万的TPS。如果是多个分区多个线程,则可以轻松达到几十万的TPS。
消息消费
Consumer将本地缓存的消息提交到消费线程中,使用业务消费逻辑对消息进行处理,处理完毕后获取到一个结果。这是真正的消息消费过程。此时Consumer的消费能力就完全依赖于消息的消费耗时和消费并发度了。如果由于业务处理逻辑复杂等原因,导致处理单条消息的耗时较长,则整体的消息吞吐量肯定不会高,此时就会导致Consumer本地缓冲队列达到上限,停止从服务端拉取消息。
总结
消息堆积的主要瓶颈在于客户端的消费能力,而消费能力由消费耗时和消费并发度决定。注意,消费耗时的优先级要高于消费并发度。即在保证了消费耗时的合理性前提下,再考虑消费并发度问题。
消费耗时
影响消息处理时长的主要因素是代码逻辑。而代码逻辑中可能会影响处理时长代码主要有两种类型:CPU内部计算型代码和I/O操作型代码。
通常情况下代码中如果没有复杂的递归和循环的话,内部计算耗时相对外部I/O操作来说几乎可以忽略。所以外部IO型代码是消息处理时长变长的主要原因,如:
- 读写远程数据库
- 读写远程Redis
- 对下游系统的http接口调用
消息并发度
一般情况下,消费者端的消费并发度由单节点线程数和节点数量共同决定,其值为单节点线程数*节点数量
。不过,通常需要优先调整单节点的线程数,若单机硬件资源达到了上限,则需要通过横向扩展来提高消费并发度。
单机线程数计算
对于一台主机中线程池中线程数的设置需要谨慎,不能盲目直接调大线程数,设置过大的线程数反而会带来大量的线程切换的开销。理想环境下单节点的最优线程数计算模型为:
C
∗
(
T
1
+
T
2
)
T
1
\frac{C*(T1+T2)}{T1}
T1C∗(T1+T2)
- C:CPU内核数
- T1:CPU内部逻辑计算耗时
- T2:外部IO操作耗时
最优线程数计算公式:
最
优
线
程
数
=
C
∗
(
T
1
+
T
2
)
T
1
=
C
∗
T
1
T
2
+
C
最优线程数=\frac{C*(T1+T2)}{T1}=C*\frac{T1}{T2}+C
最优线程数=T1C∗(T1+T2)=C∗T2T1+C
消息堆积的避免
为了避免在业务使用时出现非预期的消息堆积和消费延迟问题,需要在前期设计阶段对整个业务逻辑进行完善的排查和梳理。其中最重要的就是梳理消息的消费耗时和设置消息消费的并发度。
梳理消息的消费耗时
通过压测获取消息的消费耗时,并对耗时较高的操作的代码逻辑进行分析。梳理消息的消费耗时需要关注以下信息:
- 消息消费逻辑的计算复杂度是否过高,代码是否存在无限循环和递归等缺陷。
- 消息消费逻辑中的I/O操作是否是必须的,能否用本地缓存等方案规避。
- 消费逻辑中的复杂耗时的操作是否可以做异步化处理。如果可以,是否会造成逻辑错乱。
设置消费并发度
对于消息消费并发度的计算,可以通过以下两步实施:
-
逐步调大单个Consumer节点的线程数,并观测节点的系统指标,得到单个节点最优的消费线程数和消息吞吐量。
-
根据上下游链路的流量峰值计算出需要设置的节点数
节 点 数 = 流 量 峰 值 单 个 节 点 消 息 吞 吐 量 节点数= \frac{流量峰值}{单个节点消息吞吐量} 节点数=单个节点消息吞吐量流量峰值
消息的清理
消息被消费过后不会被清理,是被顺序存储在commitlog文件的,且消息大小不定长,所以是不可能以消息为单位进行清理的,而是以commitlog文件为单位进行清理的。
commitlog文件存在一个过期时间,默认为72小时,即三天。除了用户手动清理外,在以下情况下也会被自动清理,无论文件中的消息是否被消费过:
- 文件过期,且到达清理时间点(默认为凌晨4点)后,自动清理过期文件
- 文件过期,且磁盘空间占用率已达过期清理警戒线(默认75%)后,无论是否达到清理时间点,
- 都会自动清理过期文件
- 磁盘占用率达到清理警戒线(默认85%)后,开始按照设定好的规则清理文件,无论是否过期。
- 默认会从最老的文件开始清理
- 磁盘占用率达到系统危险警戒线(默认90%)后,Broker将拒绝消息写入