RocketMQ生产者负载均衡(轮询机制)核心原理

前言

上文已经分析了RocketMQ消费者负载均衡核心原理,我们经常会讨论消费者负载均衡的原理,我们可能忽略了生产者这端其实也有负载均衡机制。本文将分析RocketMQ生产者是如何负载均衡的。

RocketMQ生产者为什么需要负载均衡?

在RocketMQ中,队列是消息发送的基本单位。每个Topic下可能存在多个队列,因此一个生产者实例可以向不同的队列发送消息。当生产者发送消息时,如果不能均衡的将消息发送到不同的队列,那么会导致队列里的消息分布不均衡,这样最终会导致消息性能下降,因此生产者负载均衡机制也是非常重要的。

RocketMQ生产者原理分析

既然生产者负载均衡如此重要,我们看下是如何实现的。

我们通常使用如下方法发送消息:

java 复制代码
构建消息
Message msg = new Message("TopicTest",
    "TagA",
    "OrderID188",
    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
//发送消息    
SendResult sendResult = producer.send(msg);

RocketMQ发送消息的核心逻辑在DefaultMQProducerImplsendDefaultImpl

在发送消息流程利里面有一行非常关键的逻辑,selectOneMessageQueue,看方法名称就可以知道其含义,选择一个消息队列。

java 复制代码
public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
        return this.mqFaultStrategy.selectOneMessageQueue(tpInfo, lastBrokerName);
    }

里面是通过策略类来实现的。

策略类最终通过org.apache.rocketmq.client.impl.producer.TopicPublishInfo#selectOneMessageQueue(java.lang.String) 实现。

java 复制代码
public MessageQueue selectOneMessageQueue(final String lastBrokerName) {
        //生产者第一次发消息
        if (lastBrokerName == null) {
            return selectOneMessageQueue();
        } else {
            //非第一次,重试发消息的情况,
            for (int i = 0; i < this.messageQueueList.size(); i++) {
                int index = this.sendWhichQueue.incrementAndGet();
                int pos = Math.abs(index) % this.messageQueueList.size();
                if (pos < 0)
                    pos = 0;
                MessageQueue mq = this.messageQueueList.get(pos);
                //重试的情况,不取上一个broker的队列
                if (!mq.getBrokerName().equals(lastBrokerName)) {
                    return mq;
                }
            }
            return selectOneMessageQueue();
        }
    }

第一次发消息选择队列核心逻辑在 org.apache.rocketmq.client.impl.producer.TopicPublishInfo#selectOneMessageQueue()

java 复制代码
//线程安全的index
private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();

public MessageQueue selectOneMessageQueue() {
        //获取一个基础索引,每次自增1 这个全局存在TopicPublishInfo 每一个topic
        int index = this.sendWhichQueue.getAndIncrement();
        // 基础索引和 消息写队列大小 进行取模 用来实现轮训的算法
        int pos = Math.abs(index) % this.messageQueueList.size();
        if (pos < 0)
            pos = 0;
            
        return this.messageQueueList.get(pos);
    }

哈哈,这里就是生产者负载均衡轮询机制的核心逻辑了,使用到了ThreadLocal技术,sendWhichQueue为每个生产者线程维护一个自己的下标索引。

基础索引计算器,使用ThreadLocal技术针对不同的生产者线程第一次随机,后面递增,可以更加负载均衡。

java 复制代码
public class ThreadLocalIndex {
    //关键技术
    private final ThreadLocal<Integer> threadLocalIndex = new ThreadLocal<Integer>();
    private final Random random = new Random();

    public int getAndIncrement() {
        Integer index = this.threadLocalIndex.get();
        if (null == index) {
            //第一次随机
            index = Math.abs(random.nextInt());
            if (index < 0)
                index = 0;
            this.threadLocalIndex.set(index);
        }
        //第二次索引位置开始自增1
        index = Math.abs(index + 1);
        if (index < 0)
            index = 0;

        this.threadLocalIndex.set(index);
        return index;
    }
}

哈哈,有没有觉得这个实现非常巧妙了。不同的生产者线程都拥有自己的索引因子,分配队列更加均衡。

总结

本文分析了RocketMQ生产者底层的实现,设计地方有巧妙之处,值得我们学习,上面是发送非顺序消息的场景, 如果是顺序消息,我们作为使用者可以指定负载均衡策略。

java 复制代码
public SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout)
    throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    return this.sendSelectImpl(msg, selector, arg, CommunicationMode.SYNC, null, timeout);
}

RocketMQ也提供了了几个负载均衡实现。支持hash,随机

如果文章内容对您有帮助,欢迎关注我,我们一起学习。

相关推荐
uzong4 小时前
一个好的面试官,应该让候选人感到被尊重
面试
阳光是sunny6 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端
灯澜忆梦6 小时前
GO_并发编程---定时器
开发语言·后端·golang
阳光是sunny6 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
皮皮林5516 小时前
开源实力派,给本地 AI 装上深度调研能力,一张 3090 跑到 95.7 分!
后端
努力的小雨7 小时前
把 Windows 桌面图标做成一个会自己运转的小世界
后端
触底反弹7 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试
武子康7 小时前
Search Console Platform Properties 扩大 SEO 资产边界:从 Page Ranking 到 Topic Coverage(5 类误读边界 + 3 表数据层设计)
前端·人工智能·后端
腾渊信息科技公司8 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
沉默王二9 小时前
腾讯一面,我霸气反问:“你说你们在做Agent项目,说说 SubAgent、Plan 模式、Skill 调用这些你们都是怎么做的?”面试官一直在擦汗。。
面试·agent·ai编程