RocketMQ Spring Starter消费堆积引发的系统思考和处理(1)

2022-07-24 09:40:25 浏览数 (1)

在使用RocketMQ的过程中,消费堆积问题是不可避免的问题。这次借机分享下如何系统思考和解决问题,各位方家指正。

我本地已经复现了该问题,这里以一个复现的app做分享和解释。主要包含

  1. 复现问题
  2. 问题描述
  3. 问题分析
  4. 翻看源码确认问题
  5. 解决问题
    1. 临时解决 --- 变通解
    2. 长期解决 --- 提PR彻底解
  6. 周边问题确认

本文涉及rocketmq dashboard代码、rocketmq admin tool代码、broker代码、client代码、client&broker心跳源码等,建议坐地铁时翻看,有问题随时留言。

1. 复现问题:这一步靠经验吧。

https://github.com/rocketmq101/spring-issue-405

2. 问题描述

用户在使用RocketMQ Spring 2.2.1的时候发现消费堆积,异常截图如下:

消费详情一直不变,并且始终消费不到queue 0, queue 1:

消费者实例:启动了一个push消费者,消费者client却有两个。

用户使用的代码:

用户配置:

3. 初步分析

某种原因导致启动了两个消费者实例,并且会导致订阅关系不一致。

可能性1:

根据以往的经验,订阅关系不一致,会导致全部的消费者在分配queue-消费者实例的时候出现混乱,导致订阅错乱,所以有的queue没有消费者消费。

此时重温下什么是订阅关系:在一个消费者组中,消费者组-topic-tag在每个消费者实例启动时必须保持一致。

经过排查,这个消费者订阅关系一致,排除掉。

可能性2:

Push消费者和Pull消费者同时存在。导致用户使用的Push消费者只消费到了一部分queue,另一部分由Pull消费者分配了,但是没有展示。

为什么这么猜测?

首先,源自于flink消费kafka的逻辑,在flink消费kafka时,kafka-manager上是看不到消费者的。flink消费kafka实际是pull的,猜测RocketMQ和kafka这里逻辑一样。

其次,RocketMQ队列分配逻辑:每个消费者将自己注册到broker中,启动reblance时,全部的queue按照一定的原则分配给全部的消费者实例。Pull和Push消费者是否一起当作消费者分配了不同的队列呢?

这个猜测最后通过排查rocketmq-dashboad、rocketmq broker、rocketmq client的代码后确认了此逻辑。

4. 源码翻看。

4.1 RocketMQ Spring源码确认问题

翻看github issue,可以找到:https://github.com/apache/rocketmq-spring/issues/450有相似问题。这个问题目前笔者已经提PR修复了,欢迎大家使用新版本的RocketMQ Spring。

翻看源码后,确认了一个恶心的逻辑。

恶心逻辑:如果rocketmq-spring配置项中包含了下面三个配置项时,会默认启动一个DefaultLitePullConsumer实例。

代码语言:javascript复制
rocketmq.name-server=127.0.0.1:9876
rocketmq.consumer.group=test-group
rocketmq.consumer.topic=test-topic

总结:所以可以解释通现象中明明只配置了一个PushConsumer,但是启动了2个Client实例。

4.2 Broker4.9.3 源码,确认日志。

其实消费者在启动注册到Broker的时候, 会打印日志:

如果启动很久了,日志可能被删除了,重启下消费者就会看到。

5. 问题处理

如果处理呢?在配置RocketMQ Spring 时,只要以下三个配置项不同时存在就行了(这里吐槽Spring的条件注解,非常好用,也非常容易滋生暗病)

代码语言:javascript复制
rocketmq.name-server=127.0.0.1:9876
rocketmq.consumer.group=test-group
rocketmq.consumer.topic=test-topic

一般处理到这里就完了。下面是我提的两个问题:

第一个问题:如何彻底解这个问题呢?

当然是提交代码,合并到社区,新版本会彻底修掉这个bug,大家也不会再遇到这个问题。

目前代码已经合并到社区,欢迎大家阅读指正:

https://github.com/apache/rocketmq-spring/pull/459。

这个修改的指导思想是:配置项功能需要简单、明确,一个配置项就干一个事情。

第二个问题:在消费详情中,为什么Pull消费者在Dashboard中不显示消费者client和queue的关系信息呢?

实际下图的空白中,是Pull消费者消费的,却没有consumerClient。

第三个问题:在消费者实例列表中,明明Pull和Push消费者的ConsumerType不一样,为什么这里只显示了CONSUME_ACTIVELY(Pull)

CONSUME_ACTIVELY为什么表示Pull,请看代码:

第二,三个问题将在下一篇继续介绍。

0 人点赞