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

数据流通道

2022-06-06

200

0

技术:rocketmq 4.9.1 + rocketmq spring starter 2.2.1

demo功能:分享一个由rocketmq spring starter bug引起的消费堆积处理

在使用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实例。

rocketmq.name-server=127.0.0.1:9876rocketmq.consumer.group=test-grouprocketmq.consumer.topic=test-topic

 

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

 

 

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

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

图片

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

 

5. 问题处理

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

rocketmq.name-server=127.0.0.1:9876rocketmq.consumer.group=test-grouprocketmq.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,请看代码:

图片

 

 

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

 

文末推荐

笔者编写的《RocketMQ分布式消息中间件(核心原理与最佳实践)》最近jd618活动半价,欢迎大家阅读

图片

欢迎添加微信,互相学习↑↑↑ -_-

发表评论

全部评论:0条

白老虎

programming is not only to solve problems, ways to think