前言:

  RocketMq producer 在发送一条消息时候,从 producer --nameSrv -- Broker  中间经过了什么样子的数据交互

开始:

如下是 Producer 发送消息的一个demo例子:

    //1. 初始化 mq producer
        DefaultMQProducer mqProducer =new DefaultMQProducer("iscys-test");
        //2.设置nameServer 地址
        mqProducer.setNamesrvAddr("localhost:9876");
        //3. 开启mq producer,这一步是必须的,会做一些连接初始化检测工作
        mqProducer.start();
        //4.创建 Message
        Message msg = new Message("test-topis", "iscys-test".getBytes());
        //5.发送消息,设置回调,消息发送成功会回调函数
        mqProducer.send(msg, new SendCallback() {

            @Override
            public void onSuccess(SendResult sendResult) {
                //在消息发送成功之后,我们收到broker的响应通知后,会进行回调
                System.out.println("send success");
            }

            @Override
            public void onException(Throwable e) {
                System.out.println("send fail");

            }
        });

构建发送消息:

 public void send(Message msg, SendCallback sendCallback, long timeout)
        throws MQClientException, RemotingException, InterruptedException {
        try {
            //默认异步发送,超时3s
            this.sendDefaultImpl(msg, CommunicationMode.ASYNC, sendCallback, timeout);
        } catch (MQBrokerException e) {
            throw new MQClientException("unknownn exception", e);
        }
    }

从NameSrv 中获取topic 配置的相关信息,比如 broker 地址,队列数 之类的。

 private SendResult sendDefaultImpl(
        Message msg,
        final CommunicationMode communicationMode,
        final SendCallback sendCallback,
        final long timeout
    ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        this.makeSureStateOK();
        Validators.checkMessage(msg, this.defaultMQProducer);

        final long invokeID = random.nextLong();
        long beginTimestampFirst = System.currentTimeMillis();
        long beginTimestampPrev = beginTimestampFirst;
        long endTimestamp = beginTimestampFirst;
        //1.尝试取获取从NameSrv 中获取topic 相关信息
        TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
        if (topicPublishInfo != null && topicPublishInfo.ok()) {
            MessageQueue mq = null;
            Exception exception = null;
            SendResult sendResult = null;
            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();
                //2.选择一个消息队列,默认为4个,在创建新的Topic时候
                MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
                if (mqSelected != null) {
                    mq = mqSelected;
                    brokersSent[times] = mq.getBrokerName();
                    try {
                        beginTimestampPrev = System.currentTimeMillis();
                        //3.发送消息
                        sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout);
                        endTimestamp = System.currentTimeMillis();
                        this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
                        switch (communicationMode) {
                            case ASYNC:
                                return null;
                            case ONEWAY:

主要看一下如上代码第一步 尝试获取Topic 信息  tryToFindTopicPublishInfo:

    

   1. 会先从 topicPublishInfoTable 缓存中获取topic 配置信息

   2.缓存没有,就从NameSrv 中拉取。

   3.如果获取到了,则返回。

   4.NameSrv 没有得到相关到topic 信息,说明是新到topic ,则就请求获取TBW102 topic 配置信息,这个肯定能获取到,封装使用TBW102的配置。

 

    private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
        //1.从 topicPublishInfoTable 从尝试从Map中获取,如果没有获取到,请求NameSrv
        TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
        if (null == topicPublishInfo || !topicPublishInfo.ok()) {
            this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
            //2.从NameSrv 中拉取topic 信息
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
        }
       
        if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
            //3.说明获取到TOPIC 的信息
            return topicPublishInfo;
        } else {
            //4.如果第2步执行后 NameSrv 中没有topic 信息,获取默认的TBW102 topic 的信息,这个是肯定能获取到的
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
            return topicPublishInfo;
        }
    }

 

请求NameSrv 非默认的topic

 public boolean updateTopicRouteInfoFromNameServer(final String topic) {
        //1.非默认的topic ,默认Topic 为TBW102
        return updateTopicRouteInfoFromNameServer(topic, false, null);
    }

执行从NameSrv 获取topic 请求:

    1. 从NameSrv 中获取到 TBW102 的topic 信息,这个一般都是有的。

    2. 新的topic 会从NameSrv 中获取信息,如果不存在,返回false。

public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
        DefaultMQProducer defaultMQProducer) {
        try {
            if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
                try {
                    TopicRouteData topicRouteData;
                    if (isDefault && defaultMQProducer != null) {
                        // 1.如果请求的是默认的Topic 请求会走到这里
                        topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),
                            1000 * 3);
                        if (topicRouteData != null) {
                            for (QueueData data : topicRouteData.getQueueDatas()) {
                                int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
                                data.setReadQueueNums(queueNums);
                                data.setWriteQueueNums(queueNums);
                            }
                        }
                    } else {
                        // 2.新的Topic 会先从NameSrv 中获取一遍,如果NameSrv 中没有获取到,会抛出异常
                        topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
                    } 

 从NameSrv 获取到 TopicRouteData 进行如下表的缓存

          brokerAddressTable  --- 从TopicRouteData 得到broker 信息,进行brokerAddressTable 存储

          TopicPublishInfoTable --TopicRouteData 转换为topicPushInfo 存储到 topicPublishInfoTable表中

          topicRouteTable  ---存储namesrv 得到的 TopicRouteData  值 

 if (topicRouteData != null) {
                        //1.与缓存做比较,查看topic 相关信息是否有改变
                        TopicRouteData old = this.topicRouteTable.get(topic);
                        boolean changed = topicRouteDataIsChange(old, topicRouteData);
                        if (!changed) {
                            changed = this.isNeedUpdateTopicRouteInfo(topic);
                        } else {
                            log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);
                        }

                        if (changed) {
                            TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();
                            //2.broker 信息进行brokerAddrTable 表存储
                            for (BrokerData bd : topicRouteData.getBrokerDatas()) {
                                //缓存
                                this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
                            }

                            // Update Pub info
                            {
                                //3.将topicRouteData 转换为TopicPublishInfo ,用于producer
                                TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
                                //设置为true 标记这个publishInfo是可用的
                                publishInfo.setHaveTopicRouterInfo(true);
                                Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();
                                while (it.hasNext()) {
                                    Entry<String, MQProducerInner> entry = it.next();
                                    MQProducerInner impl = entry.getValue();
                                    if (impl != null) {
                                        //更新producer  topicPublishInfoTable表信息
                                        impl.updateTopicPublishInfo(topic, publishInfo);
                                    }
                                }
                            }
                                //4.consumerTable 维护用户Consumer
                            // Update sub info
                            {
                                Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
                                Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
                                while (it.hasNext()) {
                                    Entry<String, MQConsumerInner> entry = it.next();
                                    MQConsumerInner impl = entry.getValue();
                                    if (impl != null) {
                                        impl.updateTopicSubscribeInfo(topic, subscribeInfo);
                                    }
                                }
                            }
                            log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);
                            //5.topicRouteTable 表存储原始信息,用于定时任务心跳等 
                            this.topicRouteTable.put(topic, cloneTopicRouteData);
                            return true;

获取到topic信息后封装成 TopicPublishInfo:

public class TopicPublishInfo {
    private boolean orderTopic = false;
    //用来检测Topic 在Broker 真实存在的,不存在false
    private boolean haveTopicRouterInfo = false;
    //消息队列的
    private List<MessageQueue> messageQueueList = new ArrayList<MessageQueue>();
    private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();
    //请求NameSrv 返回的TOPIC 具体信息 
    private TopicRouteData topicRouteData;

 之后就是消息的发送。

 

再看producer 启动:

  说完消息发送,我们再看producer 的启动初始化:

     1.检查配置信息

     2.初始化MQClientInstance

     3.将producer 信息注册到MQClientInstance维护的producerTable 表中

     4. topicPublishInfoTable 放入默认的topic

     5.启动MQClientInstance

     6.producer 状态修改为运行状态

     7.发送心跳向broker

public void start(final boolean startFactory) throws MQClientException {
        switch (this.serviceState) {
            case CREATE_JUST:
                this.serviceState = ServiceState.START_FAILED;
                //1.检查配置
                this.checkConfig();

                if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
                    this.defaultMQProducer.changeInstanceNameToPID();
                }
                //2.初始化 MQClientInstance producer 交互的重要信息组件
                this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQProducer, rpcHook);
                // 3.producerGroup 注册到 producerTable 表中 k= producerName v = this
                boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
                if (!registerOK) {
                    this.serviceState = ServiceState.CREATE_JUST;
                    throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()
                        + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
                        null);
                }
                //4.topicPublishInfoTable 表中默认放入TBW102
                this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());

                if (startFactory) {
                    //5.启动工厂
                    mQClientFactory.start();
                }

                log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
                    this.defaultMQProducer.isSendMessageWithVIPChannel());
                //6.状态改变为启动状态
                this.serviceState = ServiceState.RUNNING;
                break;
            case RUNNING:
            case START_FAILED:
            case SHUTDOWN_ALREADY:
                throw new MQClientException("The producer service state not OK, maybe started once, "
                    + this.serviceState
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
                    null);
            default:
                break;
        }
        //7.发送心跳检测向所有的broker
        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
    }

 

我们主要看  MQClientInstance 创建以及启动:

public class MQClientInstance {
    private final static long LOCK_TIMEOUT_MILLIS = 3000;
    private final Logger log = ClientLogger.getLog();
    //配置信息
    private final ClientConfig clientConfig;
    //创建索引
    private final int instanceIndex;
    //pid
    private final String clientId;
    private final long bootTimestamp = System.currentTimeMillis();
    //producer 维护的group 表,producer 会注册这张表,如上代码启动的第3步
    private final ConcurrentMap<String/* group */, MQProducerInner> producerTable = new ConcurrentHashMap<String, MQProducerInner>();
    //consumer 维护的group 表
    private final ConcurrentMap<String/* group */, MQConsumerInner> consumerTable = new ConcurrentHashMap<String, MQConsumerInner>();
    //admin 维护的group 表
    private final ConcurrentMap<String/* group */, MQAdminExtInner> adminExtTable = new ConcurrentHashMap<String, MQAdminExtInner>();
    //Netty 配置类
    private final NettyClientConfig nettyClientConfig;
    //Netty 客户端
    private final MQClientAPIImpl mQClientAPIImpl;
    //admin
    private final MQAdminImpl mQAdminImpl;
    //从NameSrv 拉取到Topic 相关信息的原始值放在这个表里面
    private final ConcurrentMap<String/* Topic */, TopicRouteData> topicRouteTable = new ConcurrentHashMap<String, TopicRouteData>();
    private final Lock lockNamesrv = new ReentrantLock();
    private final Lock lockHeartbeat = new ReentrantLock();
    //broker 地址表
    private final ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable =
        new ConcurrentHashMap<String, HashMap<Long, String>>();
    //broker version 表
    private final ConcurrentMap<String/* Broker Name */, HashMap<String/* address */, Integer>> brokerVersionTable =
        new ConcurrentHashMap<String, HashMap<String, Integer>>();
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "MQClientFactoryScheduledThread");
        }
    });
    //请求处理器
    private final ClientRemotingProcessor clientRemotingProcessor;
    //拉去消息服务
    private final PullMessageService pullMessageService;
    //负载均衡服务
    private final RebalanceService rebalanceService;
    //默认Group 的producer 服务
    private final DefaultMQProducer defaultMQProducer;
    //consumer 状态管理
    private final ConsumerStatsManager consumerStatsManager;
    private final AtomicLong sendHeartbeatTimesTotal = new AtomicLong(0);
    //MqClientInstance 启动状态
    private ServiceState serviceState = ServiceState.CREATE_JUST;
    private DatagramSocket datagramSocket;
    private Random random = new Random();

启动MQClientInstance:

  public void start() throws MQClientException {

        synchronized (this) {
            switch (this.serviceState) {
                case CREATE_JUST:
                    this.serviceState = ServiceState.START_FAILED;
                    // 如果没有配置nameSrv 默认从java 配置的propertues 参数路由地址获取信息,阿里就是这么使用的,nameSrv 的获取就相当于一个web 服务
                    if (null == this.clientConfig.getNamesrvAddr()) {
                        this.mQClientAPIImpl.fetchNameServerAddr();
                    }
                    // Start request-response channel
                    //初始化Netty 客户端
                    this.mQClientAPIImpl.start();
                    // Start various schedule tasks
                    //开启一些定时任务
                    this.startScheduledTask();
                    // Start pull service
                    this.pullMessageService.start();
                    // Start rebalance service
                    this.rebalanceService.start();
                    // Start push service  默认group的 producer 启动
                    this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
                    log.info("the client factory [{}] start OK", this.clientId);
                    this.serviceState = ServiceState.RUNNING;
                    break;
                case RUNNING:
                    break;
                case SHUTDOWN_ALREADY:
                    break;
                case START_FAILED:
                    throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
                default:
                    break;
            }
        }
    }

 

posted on 2020-06-16 23:37  iscys  阅读(739)  评论(0编辑  收藏  举报