RocketMQ消息生产者是如何选择Broker示例详解

 更新时间:2022年11月29日 10:32:21   作者:梦想实现家_Z  
这篇文章主要为大家介绍了RocketMQ消息生产者是如何选择Broker示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

RocketMQ中为,我们创建消息生产者时,只需要设置NameServer地址,消息就能正确地发送到对应的Broker中,那么RocketMQ消息生产者是如何找到Broker的呢?如果有多个Broker实例,那么消息发送是如何选择发送到哪个Broker的呢?

从NameServer查询Topic信息

通过Debug消息发送send()方法,我们最终可以定位到DefaultMQProducerImpl.sendDefaultImpl()这个方法,并且我们找到了最关键的Topic信息:

TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());

这个方法就是通过topicNameServer拉出对应的Broker信息:

    private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
        TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
        if (null == topicPublishInfo || !topicPublishInfo.ok()) {
            this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
        }
        if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
            return topicPublishInfo;
        } else {
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
            return topicPublishInfo;
        }
    }

1.一开始的话,是从当前缓存中找Topic信息,第一次肯定是找不到的;

2.找不到Topic信息,那么就调用updateTopicRouteInfoFromNameServer(topic)NameServer拉对应的信息,如果拉到了就更新到缓存中;

3.如果依然找不到Topic信息,说明没有任何Broker上面是有这个Topic的;但是我们还要拉开启了自动创建Topic配置的Broker信息,通过updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer)实现;

生产者客户端会从两个地方获取Broker信息,第一个就是从内存缓存中获取,第二个就是从NameServer中获取。从NameServer中分两次获取,一次是获取存在的Topic对应的Broker信息,第二次是获取还没有创建出来的Topic对应的Broker信息;

如何选择Broker

当客户端拿到了Topic对应的Broker信息后,它是如何选择目标Broker的呢?继续向下看,我们找到了关键代码:

int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
            int times = 0;
            String[] brokersSent = new String[timesTotal];
            for (; times < timesTotal; times++) {
                String lastBrokerName = null == mq ? null : mq.getBrokerName();
                MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
                if (mqSelected != null) {
                    mq = mqSelected;
                    brokersSent[times] = mq.getBrokerName();
                  ......

1.如果是同步发送消息,那么【总的发送次数】=1+【重试次数】,如果是异步发送,默认是1;我们当前是同步模式,所以会存在重试;

2.选择Broker的关键代码就在selectOneMessageQueue()方法中,通过前面拿到的topicPublishInfo作为参数,lastBrokerName作为额外的考虑参数;

追踪代码,我们进入MQFaultStrategy.selectOneMessageQueue()中:

    public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
        if (this.sendLatencyFaultEnable) {
            try {
                int index = tpInfo.getSendWhichQueue().incrementAndGet();
                for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) {
                    int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size();
                    if (pos < 0)
                        pos = 0;
                    MessageQueue mq = tpInfo.getMessageQueueList().get(pos);
                    if (latencyFaultTolerance.isAvailable(mq.getBrokerName()))
                        return mq;
                }
                final String notBestBroker = latencyFaultTolerance.pickOneAtLeast();
                int writeQueueNums = tpInfo.getQueueIdByBroker(notBestBroker);
                if (writeQueueNums > 0) {
                    final MessageQueue mq = tpInfo.selectOneMessageQueue();
                    if (notBestBroker != null) {
                        mq.setBrokerName(notBestBroker);
                        mq.setQueueId(tpInfo.getSendWhichQueue().incrementAndGet() % writeQueueNums);
                    }
                    return mq;
                } else {
                    latencyFaultTolerance.remove(notBestBroker);
                }
            } catch (Exception e) {
                log.error("Error occurred when selecting message queue", e);
            }
            return tpInfo.selectOneMessageQueue();
        }
        return tpInfo.selectOneMessageQueue(lastBrokerName);
    }

1.如果开启了延迟故障规避,那么执行规避策略;

  • 1.1:轮询找一个Broker,该Broker要么不在规避名单内,要么已经度过了规避期(发送消息失败会将目标Broker放进规避名单,沉默一段时间);
  • 1.2:如果所有的Broker都没有度过规避期,那么从比较好的那一部分Broker里面找一个出来;
  • 1.3:如果依然没有找到合适的Broker,那么就随机选一个Broker

2.否则就随机选一个Broker

下面我们来看一下随机发送的策略是怎么实现的:

    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);
                if (!mq.getBrokerName().equals(lastBrokerName)) {
                    return mq;
                }
            }
            return selectOneMessageQueue();
        }
    }
    public MessageQueue selectOneMessageQueue() {
        int index = this.sendWhichQueue.incrementAndGet();
        int pos = Math.abs(index) % this.messageQueueList.size();
        if (pos < 0)
            pos = 0;
        return this.messageQueueList.get(pos);
    }

1.如果第一次发送消息,那么通过自增求余的方式从列表中找一个Broker,其实就是轮询方式;

2.如果不是第一次发送消息,那么会尽可能避开上一次的Broker服务,也是为了让Broker服务负载均衡;

3.如果没有避开上一次的Broker,那么再向后找另一个Broker;除非只有一个Broker服务,否则会尽可能避开上次发送的Broker

小结

通过源码分析,我们已经知道了生产者是如何选择目标Broker的了:

1.第一次发消息,通过轮询的方式选择Broker

2.后续发消息会规避上次的Broker,同样采用轮询的方式选择Broker

3.在消息发送过程中,存在一个Broker规避列表,用户可以通过setSendLatencyFaultEnable(true)开启故障规避策略,客户端会尽可能选择不在规避列表中的Broker,如果所有的Broker都在规避列表中,那么会选择一个相对比较好的Broker来用;

以上就是RocketMQ消息生产者是如何选择Broker示例详解的详细内容,更多关于RocketMQ消息生产者Broker的资料请关注脚本之家其它相关文章!

相关文章

  • ThreadLocal导致JVM内存泄漏原因探究

    ThreadLocal导致JVM内存泄漏原因探究

    ThreadLocal是JDK提供的线程本地变量机制,但若使用不当可能导致内存泄漏。正确的使用方式是在使用完后及时remove,或者使用弱引用等手段避免强引用导致的内存泄漏。在多线程编程中,合理使用ThreadLocal可以提高并发性能,但也需要注意其潜在的内存泄漏问题
    2023-04-04
  • Java解决程序包不存在的问题解决

    Java解决程序包不存在的问题解决

    在Java编程中,我们可以使用Maven或Gradle等构建工具来管理依赖库,本文主要介绍了Java解决程序包不存在的问题解决,具有一定的参考价值,感兴趣的可以了解 一下
    2023-12-12
  • mybatis实现一对一关联映射实例代码

    mybatis实现一对一关联映射实例代码

    这篇文章主要给大家介绍了关于mybatis实现一对一关联映射的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-11-11
  • SpringBoot中的bean管理示例详解

    SpringBoot中的bean管理示例详解

    这篇文章主要介绍了SpringBoot中的bean管理,本文结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • Java并行执行任务的几种方案小结

    Java并行执行任务的几种方案小结

    这篇文章主要介绍了Java并行执行任务的几种方案小结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringBoot3.x中spring.factories SPI 服务发现机制的改变问题小结

    SpringBoot3.x中spring.factories SPI 服务发现机制的改变问题小结

    spring.factories其实是SpringBoot提供的SPI机制,底层实现是基于SpringFactoriesLoader检索ClassLoader中所有jar引入的META-INF/spring.factories文件,这篇文章主要介绍了SpringBoot3.x中spring.factories SPI 服务发现机制的改变,需要的朋友可以参考下
    2023-05-05
  • 新手了解java 类,对象以及封装基础知识

    新手了解java 类,对象以及封装基础知识

    JS是一门面向对象语言,其对象是用prototype属性来模拟的,本文介绍了如何封装JS对象,具有一定的参考价值,下面跟着小编一起来看下吧,希望对你有所帮助
    2021-07-07
  • Java对象创建内存案例解析

    Java对象创建内存案例解析

    这篇文章主要介绍了Java对象创建内存案例解析,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Fastjson反序列化随机性失败示例详解

    Fastjson反序列化随机性失败示例详解

    这篇文章主要为大家介绍了Fastjson反序列化随机性失败示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Mybatis逆向工程运行代码实例

    Mybatis逆向工程运行代码实例

    这篇文章主要介绍了Mybatis逆向工程运行代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04

最新评论