RocketMQ 消费者订阅topic问题

3,017 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

RocketMQ 消费者订阅topic问题

集群消费

广播消费

消息消费订阅消息

消费者组需要订阅多个 Topic 时,可以重复调用 subscribe方法,指定不同的 topic

下面的代码中, 一个消费者组 same_group, 需要订阅 Topic1Topic2

消费者订阅消息一致

一般来说一个进程只写一个消费组代码,如何写的两个消费者代码,订阅 topic 信息完全一致时,

消息者1

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer( "same_group");
consumer.setNamesrvAddr("10.227.12.225:9876");
consumer.subscribe("Topic1", "*");
consumer.subscribe("Topic2", "*");
consumer.setInstanceName("instance1")
consumer.registerMessageListener(...); //注册监听器,略
consumer.start();

消费者2

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer( "same_group");
consumer.setNamesrvAddr("10.227.12.225:9876");
consumer.subscribe("Topic1", "*");
consumer.subscribe("Topic2", "*");
consumer.setInstanceName("instance2")
consumer.registerMessageListener(...); //注册监听器,略
consumer.start();

区别在于setInstanceName,但是两个消费者订阅信息完全一致,会正常进行 rebalance 会提示消费者订阅消息相同:

Same subscription in the same group of consumer
Rebalance OK

订阅topic不一致

假设一个进程启动两个消费者,一个订阅一个 topic1 ,一个订阅一个 topic2

消费者1

只订阅 topic1

DefaultMQPushConsumer consumer1 = new DefaultMQPushConsumer( "no_same_group");
consumer1.setNamesrvAddr("10.227.12.225:9876");
consumer1.setInstanceName("instance1");
consumer1.subscribe("Topic1", "*");
...

消费者2

只订阅 Topic2

DefaultMQPushConsumer consumer2 = new DefaultMQPushConsumer( "no_same_group");
consumer2.setNamesrvAddr("10.227.12.225:9876");
consumer2.setInstanceName("instance2");
consumer2.subscribe("Topic2", "*");
...

消费者1 和消费者2属于同一个消费者组 no_same_group,但是一个 topic1,另外一个订阅 topic2

会提示订阅信息不一致的警告,订阅信息不一致,会导致分区无法正常分配

WARN: Different subscription in the same group of consumer!!!

生产者发送已经分配给消费者的队列的消息依然可以继续消费,但是发送到未分配到队列则无法进行消费,也就是说,生产者发送的消息,部分可以消费,部分无法消费。

为啥会报错

看源码中 org.apache.rocketmq.broker.processor.PullMessageProcessor#processRequest: 将 topic 订阅信息找出来,如果没有找到,所以报消费订阅不存在的错误。

subscriptionData = consumerGroupInfo.findSubscriptionData(requestHeader.getTopic());
if (null == subscriptionData) {
  log.warn("the consumer's subscription not exist, group: {}, topic:{}", requestHeader.getConsumerGroup(), requestHeader.getTopic());
  response.setCode(ResponseCode.SUBSCRIPTION_NOT_EXIST);
  response.setRemark("the consumer's subscription not exist" + FAQUrl.suggestTodo(FAQUrl.SAME_GROUP_DIFFERENT_TOPIC));
  return response;
}

那么为啥 topic 信息会找不到呢?

消费者的订阅信息在 broker 中时以 group 来分组的,具体查看 org.apache.rocketmq.broker.client.ConsumerManager:

private final ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTable =
  new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);

也就是说,集群中每个消费者在向 broker 注册订阅信息的时候会相互覆盖对方的订阅信息。 这样就是为啥同一个消费者订阅关系都不一样,出现了订阅信息覆盖的问题。