RocketMQ负载均衡机制解析!

相关文章:

文章首发到公众号:月伴飞鱼

文章内容收录到个人网站,方便阅读:hardyfish.top/

文章内容收录到个人网站,方便阅读:hardyfish.top/

觉得有收获,希望帮忙点赞,转发下哈,谢谢,谢谢!

资料分享

RocketMQ技术内幕:

深入理解Kafka:核心设计与实践原理

消费者在消费消息的时候,需要知道从Broker的哪一个消息队列中去获取消息。

所以,在消费者端必须要做负载均衡,即Broker端中多个消费队列分配给同一个消费者组中的哪些消费者消费。

RocketMQ中,在消费者端有一个:Rebalance负载均衡组件

  • 他负责相对均匀的给消费者分配需要拉取的队列信息。

消费者负载均衡

指为消费组下的每个消费者分配订阅主题下的消费队列,分配了消费队列消费者就可以知道去消费哪个消费队列上面的消息。

  • 这里针对集群模式 ,因为广播模式,所有的消息队列可以被消费组下的每个消费者消费不涉及负载均衡。

而集群模式一个消息队列同一时间只能分配给组内的一个消费者进行消费。

RocketMQ5.0以前是按照队列 粒度进行负载均衡的,5.0以后提供了按消息粒度进行负载均衡。

队列粒度负载均衡

队列粒度负载均衡策略中,同一消费者组内的多个消费者将按照队列粒度消费消息,每个队列只能被其中一个消费者消费。

队列粒度负载均衡是在每个消费者端进行的,并不是由某个节点统一进行负载均衡之后将分配结果通知到每个消费者。

消费者增加或者减少会影响消息队列的分配,所以Broker需要感知消费者的上下线情况。

消费者在启动时会向所有的Broker发送心跳包进行注册,通知Broker消费者上线,下线的时候也会向Broker发送取消注册的请求。

Broker会维护消费者信息的注册信息,在消费者发生变更时会通知消费者进行负载均衡。

Rebalance触发时机

消费者启动时触发:

消费者在启动时会进行一次负载均衡,为自己分配消息队列。

Broker发现消费组变更时触发:

处于以下两种情况之一时会被判断为消费组发生了变化,需要进行负载均衡:

  • 某个消费组内有新的消费者向Broker进行了注册。

    • 比如某个消费组原来有两个消费者,现在新增了一个消费者,新增的消费者启动时会向Broker发送注册请求。
  • 消费组订阅的主题信息发生了变化。

    • 比如消费组新增订阅了某个主题或者取消某个主题的订阅,会被判断为主题订阅信息发生了变化。

被判定为变化之后,会触发变更事件,向该消费者下的所有消费者发送发送变更请求,通知组下每个消费者进行负载均衡。

Broker收到消费者下线时触发:

如果有消费者向Broker发送UNREGISTER_CLIENT取消注册请求,并且开启了允许通知变更,会触发变更事件。

变更事件同上,Broker会通知该消费者组下的所有消费者进行一次负载均衡。

消费者定时触发:

消费者本身也会定时执行负载均衡,默认是20s执行一次。

消息粒度负载均衡

RocketMQ5.0之后,增加了消息粒度负载均衡策略,默认且仅使用消息粒度负载均衡策略。

消息粒度负载均衡策略中,同一消费组内的多个消费者将按照消息粒度平均分摊主题中的所有消息。

  • 即同一个队列中的消息,可被平均分配给组内多个消费者共同消费。

消息粒度负载均衡策略保证同一个队列的消息可以被组内多个消费者共同处理。

但是该策略使用的消息分配算法结果是随机的,不能指定消息被哪一个特定的消费者处理。

  • 当消费者获取到某条消息后,服务端会对该消息加锁,保证该消息对其他消费者不可见,直到消息消费成功或者超时。

所以多个消费者同时消费同一个消息队列中的消息,服务端也可以保证消息不会被多个消费者重复消费。

消息粒度负载均衡策略适用于绝大多数在线处理的业务场景,对于流式处理、聚合计算等场景,更适合队列粒度的负载均衡策略。

执行流程

负载均衡服务执行逻辑在doRebalance函数,里面会对每个消费者组执行负载均衡操作。

consumerTable这个map对象里存储了消费者组对应的的消费者实例。

java 复制代码
private ConcurrentMap<String/* group */, MQConsumerInner> consumerTable = new ConcurrentHashMap<String, MQConsumerInner>();
 
public void doRebalance() {
    //每个消费者组都有负载均衡
    for (Map.Entry<String, MQConsumerInner> entry : this.consumerTable.entrySet()) {
        MQConsumerInner impl = entry.getValue();
        if (impl != null) {
            try {
                impl.doRebalance();
            } catch (Throwable e) {
                log.error("doRebalance exception", e);
            }
        }
    }
}

由于每个消费者组可能会消费很多topic,每个topic都有自己的不同队列,最终是按topic的维度进行负载均衡。

java 复制代码
public void doRebalance(final boolean isOrder) {
    Map<String, SubscriptionData> subTable = this.getSubscriptionInner();
    if (subTable != null) {
        for (final Map.Entry<String, SubscriptionData> entry : subTable.entrySet()) {
            final String topic = entry.getKey();
            try {
                //按topic维度执行负载均衡
                this.rebalanceByTopic(topic, isOrder);
            } catch (Throwable e) {
                if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
                    log.warn("rebalanceByTopic Exception", e);
                }
            }
        }
    }
    this.truncateMessageQueueNotMyTopic();
}

最终负载均衡逻辑处理的实现在:

  • org.apache.rocketmq.client.impl.consumer.RebalanceImpl#rebalanceByTopic

其中分为广播消息和集群消息模型两种情况处理。

负载均衡核心功能的主流程,主要做了4件事情:

负载均衡策略原理

负载均衡策略顶层接口:

java 复制代码
/**
 * Strategy Algorithm for message allocating between consumers
 */
public interface AllocateMessageQueueStrategy {
 
    /**
     * Allocating by consumer id
     * 给消费者id分配消费队列
     */
    List<MessageQueue> allocate(
        final String consumerGroup, //消费者组
        final String currentCID, //当前消费者id
        final List<MessageQueue> mqAll, //所有的队列
        final List<String> cidAll //所有的消费者
    );
 
}

他默认共有7种负载均衡策略实现。

最常用的两种平均分配算法:

AllocateMessageQueueAveragely

是用总数除以消费者个数,余数按消费者顺序分配给消费者。

AlocateMessageQueueAveragelyByCircle

轮流一个一个分配。

参考:

rocketmq.apache.org/zh/docs/fea...

相关推荐
红尘散仙15 分钟前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
来杯@Java1 小时前
图书管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·mybatis·课程设计
卷毛的技术笔记2 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
编程大师哥2 小时前
匿名函数 lambda + 高阶函数
java·python·算法
会编程的土豆2 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木2 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
Cosolar2 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
adrninistrat0r2 小时前
Java调用链MCP分析工具
java·python·ai编程
喵个咪2 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
噜噜噜阿鲁~3 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言