RocketMQ一个实例能消费另一个就不能消费?

RocketMQ一个实例能消费另一个就不能消费?

因为RocketMQ的消费模型是这样的:

RocketMQ有一条硬性约束:同一个ConsumerGroup内的所有实例,订阅关系必须完全一致。

假设你有两个业务消费者:订单消费者订阅了TOPIC_ORDER,库存消费者订阅了TOPIC_STOCK,但它们错误地使用了同一个组名GROUP_COMMON,就会同时触发以下两个机制:

Broker端每个消费组只保存一份订阅关系。消费者默认每隔30秒向Broker发送一次心跳,心跳中携带自己的订阅集合。Broker收到后会直接用这份集合覆盖该组的订阅关系。

被覆盖掉的那个Topic,其拉取请求会被Broker以订阅关系不存在为由拒绝。表现出来就是:

Broker看到GROUP_COMMON下有两个在线实例,执行Rebalance时,会把TOPIC_ORDER和TOPIC_STOCK的队列混合分摊给这两个实例。

结果可能是:库存消费者被分配到了TOPIC_ORDER的部分队列,但它根本没有订阅这个Topic,这些队列上的消息就无人消费,造成积压。

这段代码启动不会报错,两个消费者都能正常起来,客户端日志也看不出异常,这正是这个坑最隐蔽的地方。

如果你的代码不是上述问题,还有一种情况也会造成Topic之间互相影响:

Push模式的消费流程是:拉取线程将消息拉回本地缓存→切成消费任务提交给消费线程池→Listener在线程池中执行业务逻辑。这个线程池默认20个线程,是实例级别共享的。

如果TOPIC_ORDER的处理逻辑中有慢SQL、外部接口超时或数据库连接等待,20个线程被占满后,TOPIC_STOCK的消息就只能排队,客户端流控还会暂停拉取,这个和消费组无关的。

1.检查组名是否相同(最高概率):搜索代码中newDefaultMQPushConsumer()的入参,确认两个消费者是否使用了相同的Group名称。

2.查看Broker端订阅关系:使用mqadminconsumerConnection-gGROUP_NAME命令,观察该组的在线实例数及每个实例上报的订阅关系。正常情况下,同组所有实例的订阅应完全一致。

3.检查是否单实例多Topic:确认是否存在一个Consumer实例订阅了多个Topic且共用阻塞型Listener的情况。

4.打线程栈定位阻塞:使用jstack导出线程栈,搜索ConsumeMessageThread_开头的线程,查看它们卡在哪个业务调用上。

5.查看消费耗时与堆积:使用mqadminconsumerStatus-gGROUP_NAME命令,判断默认的20个消费线程是否已被业务耗时打满。

ConsumerGroup的设计意图是:一组订阅关系完全相同、消费逻辑完全相同的实例集合,用于水平扩容和负载分摊。它不是用来给不同业务做逻辑分类的。

最佳实践建议: