实验(一)
实验现象
准备
创建的topicA、topicB,每个都是16个消息队列;
创建了同一个消费者组:ntm-hxy-group
默认消息模式:负载均衡
消费者一(clientId1)
先启动一个消费者一:clientId:192.168.31.182@10962#386194072446460
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 concurrentlyPushConsumer2 = createConcurrentlyPushConsumer("ntm-hxy-group", nameAddress, "topicA", "topicB"); }
这个clientId1 订阅了topicA、topicB,属于组ntm-hxy-group
这里代表订阅的clientId的数量
上图订阅关系:client1订阅了两个topic,自动添加了一个重试的topic
该消费者组ntm-hxy-group消费队列的情况如下:
这是重试topic,只有一个消息队列,由clientId1消费
topicA所有16个队列,都由clientId消费
topicB 所有16个消息队列都由clientId1 消费
由此可见:两个topic都被这一个clientId1占用了,还有一个重试topic也是被该clinetId1占用
再启动一个消费者(clientId2)
在启动一个clientId2,同一个group,只订阅topicA。clientId:192.168.31.182@11084#386532403737756
控制台界面:
同一个消费者组group,clientId变为了两个
topic只有topicA了,也就是最新订阅信息,但是clientId还有两个。
此时,该消费者组ntm-hxy-group消费队列的情况如下:
如上图,重试消息队列消费对象没有变,因为重试topic是服务端(broker)基于group产生的,所以一直和第一个启动的clientId绑定的,也就是重试队列会一直由第一个clientId消费,不会由于心跳引起订阅的改变而改变。
也就是每个clientId可以消费topicA一半的消息,但是此时topicB丢失了,也就是发送给topicB的消息会被堆积,不能被消费了。
下次clientId1发送心跳时,topicB又订阅上了,此时的情况如下:
这个重试group跟心跳无关,还是clientId1消费没变。
原因:虽然client1发送心跳时,订阅信息刷新了,topicA和topicB都订阅了,但是消费队列绑定关系是根据group来进行替换的,因为负载均衡时关键两点:
findConsumerIdList 方法接受两个参数:Topic 主题和 ConsumerGroup 消费组
拿mqSet、cidAll进行默认的平均负载均衡策略进行负载均衡时,逻辑如下:
这个方法默认负载均衡策略具体实现如下:
public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll, List<String> cidAll) { if (currentCID == null || currentCID.length() < 1) { throw new IllegalArgumentException("currentCID is empty"); } if (mqAll == null || mqAll.isEmpty()) { throw new IllegalArgumentException("mqAll is null or mqAll empty"); } if (cidAll == null || cidAll.isEmpty()) { throw new IllegalArgumentException("cidAll is null or cidAll empty"); } List<MessageQueue> result = new ArrayList<MessageQueue>(); if (!cidAll.contains(currentCID)) { log.info("[BUG] ConsumerGroup: {} The consumerId: {} not in cidAll: {}", consumerGroup, currentCID, cidAll); return result; } int index = cidAll.indexOf(currentCID); int mod = mqAll.size() % cidAll.size(); int averageSize = mqAll.size() <= cidAll.size() ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size() + 1 : mqAll.size() / cidAll.size()); int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod; int range = Math.min(averageSize, mqAll.size() - startIndex); for (int i = 0; i < range; i++) { result.add(mqAll.get((startIndex + i) % mqAll.size())); } return result; }