实验(二)
实验步骤
准备
创建的topicA、topicB,每个都是16个消息队列;
创建了同一个消费者组:ntm-hxy-group
默认消息模式:负载均衡
消费者启动
消费者启动公共方法如下:
/** * 消费者启动 * @param pushConsumerGroup 消费者组 * @param namesrvAddr nameServer地址 * @param topic topic * @param topic topic2 * @return {@link DefaultMQPushConsumer} * @author Green * @date 2022/4/13 4:24 下午 */ public static DefaultMQPushConsumer createConcurrentlyPushConsumer(String pushConsumerGroup, String namesrvAddr, String topic, String topic2) throws Exception { DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer(pushConsumerGroup); pushConsumer.setNamesrvAddr(namesrvAddr); pushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); // 过滤方式一、TAG方式 pushConsumer.subscribe(topic, "*"); if (topic2 != null) { pushConsumer.subscribe(topic2, "*"); } // 并发消息监听接口 pushConsumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { System.out.println("test!!!"); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); pushConsumer.start(); return pushConsumer; }
启动第一个消费者(clientId1):
1 /** 2 * 消费者启动测试 3 * @param args 4 * @author Green 5 * @date 2022/4/13 4:25 下午 6 */ 7 public static void main(String[] args) throws Exception { 8 String nameAddress = "192.168.11.180:9876;192.168.11.181:9876;192.168.11.182:9876"; 9 DefaultMQPushConsumer concurrentlyPushConsumer2 = 10 createConcurrentlyPushConsumer("ntm-hxy-group", nameAddress, "topicA", "topicB"); 11 12 }
启动第二个消费者(clientId2):
/** * 消费者启动测试 * @param args * @author Green * @date 2022/4/13 4:25 下午 */ public static void main(String[] args) throws Exception { String nameAddress = "192.168.11.180:9876;192.168.11.181:9876;192.168.11.182:9876"; DefaultMQPushConsumer concurrentlyPushConsumer1 = createConcurrentlyPushConsumer("ntm-hxy-group", nameAddress, "topicA", null); }
消费者1、2属于同一个消费组,消费者1订阅了topicA、topicB,消费者2只订阅了topicA
打断点
因为需要在idea中启动两个main方法,所以每个断点都采用多线程的方式,如下:
断点位置如下:
①:org.apache.rocketmq.client.impl.consumer.RebalanceImpl#rebalanceByTopic line:261
这个目的是为了看,当前消费者负载均衡时,通过topic得到的消息队列mqSet、通过group得到的所有client的集合cidAll的值。
②org.apache.rocketmq.client.impl.consumer.RebalanceImpl#rebalanceByTopic line:293
因为在上图箭头出,就是采用负载均衡策略,得到该消费者分配的消息队列,所以这个断点就是看具体分配的消息队列有哪些。
结论
为了解决实验(一)博客最后的疑问:
因为client1订阅了topicA、topicB,所以本地缓存中有topicA和topicB队列信息,断点①是通过本地缓存得到消息队列信息mqSet的,如下:
- 在client1中通过断点①可以得到topicA的16个消息队列,通过group可以得到2个clientId,所以在断点②处分配到了8个队列进行消费;
- 在client1中通过断点①可以得到topicB的16个消息队列,通过group可以得到2个clientId,所以在断点②处分配到了8个队列进行消费;
因为client2只订阅了topicA,本地缓存中只有topicA的队列信息,如下:
- 在client2中通过断点①可以得到topicA的16个消息队列,通过group可以得到2个clientId,所以在断点②处分配到了8个队列进行消费;
- 在client2中因为本地缓存并没有topicB信息,所以断点①处得到的mqSet是空的,也就不会进行负载均衡操作进行分配,所以这就导致了topicB的另外8个队列并没有消费者进行消费,会一直堆积;
注意:虽然心跳会不断的修改服务端订阅信息(即远程clientId订阅的topic会改变,有时候有topicA、topicB,有时候只有topicA),但是各个消费端clientId本地缓存的订阅信息是不会变的,所以客户端负载均衡绑定的消费队列不会变的。