手撕RocketMQ源码——AllocateMessageQueueStrategy

引言

  • 集群模式消费、推模式下的消息队列的负载均衡算法
  • 策略模式
  • 包括一致性hash、平均分配、平均轮询分配、自定义消费队列、按机房hash算法实现

AllocateMessageQueueAveragely

平均分配的实现算法

  • 如果消费者的个数可以除尽队列的个数,那么就完全平均分。
  • 如果不能除尽。那么靠前的消费者多消费一个队列,靠后的消费平均数个队列。
  • 如果消费者的个数大于队列的个数,那么靠前的消费者消费一个队列,后面的不消费。

举个不能除尽的例子: 15个消费队列,6个消费者,那么平均数是2。剩余3个队列需要给前三个消费者消费。
那么分配下来是。3,3,3,2,2,2。

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;
        }
        // 自己是当前的第几个。 cidALL和MQALL在外层以及排序
        int index = cidAll.indexOf(currentCID);
        // 如果mod不为零,说明不能完全平均分。
        int mod = mqAll.size() % cidAll.size();
        // 如果队列的长度小于消费者的个数,那么就一个一个队列,屁股后面的就没有队列消费。
        // 如果mod为0的话 那么大家完全平均分
        // 如果mod不为0,那么当前消费者所处的位置小于mod,那就要多负载一个队列。 大于mod就消费是平均数
        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;
    }

你可能感兴趣的:(RocketMQ源码分析)