rocketmq的同步消息发送源码分析

171 阅读3分钟

本章将简单分析下rocketmq的消息发送源码,如果错误的地方,欢迎指出!源码版本为release-5.2.0

一:整体概括

image.png

二:具体流程图

image.png

三:详细源码解析

以下是针对同步消息的发送的源码详细讲解

消息的发送类DefaultMQProducer

org.apache.rocketmq.client.producer.DefaultMQProducer#send(org.apache.rocketmq.common.message.Message)

@Override
public SendResult send(
    Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    //withNamespace方法作用 为消息的topic添加命名空间
    msg.setTopic(withNamespace(msg.getTopic()));
    // 开启自动批处理
    if (this.getAutoBatch() && !(msg instanceof MessageBatch)) {
        return sendByAccumulator(msg, null, null);
    } else {
        //正常同步消息发送
        return sendDirect(msg, null, null);
    }
}

继续跟

org.apache.rocketmq.client.producer.DefaultMQProducer#sendDirect

public SendResult sendDirect(Message msg, MessageQueue mq,
    SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
    // send in sync mode
    if (sendCallback == null) {
        if (mq == null) {
            //同步发送消息,不指定messagequeue
            return this.defaultMQProducerImpl.send(msg);
        } else {
            //同步发送消息,不指定messagequeue
            return this.defaultMQProducerImpl.send(msg, mq);
        }
    } else {
        //异步消息
        if (mq == null) {
            this.defaultMQProducerImpl.send(msg, sendCallback);
        } else {
            this.defaultMQProducerImpl.send(msg, mq, sendCallback);
        }
        return null;
    }
}

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#send(org.apache.rocketmq.common.message.Message)

public SendResult send(
    Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    return send(msg, this.defaultMQProducer.getSendMsgTimeout());
}

```
public SendResult send(Message msg,
    long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null, timeout);
}
```

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl 发送消息的逻辑核心

private SendResult sendDefaultImpl(
    Message msg,
    final CommunicationMode communicationMode,
    final SendCallback sendCallback,
    final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    this.makeSureStateOK();
    // 校验消息 topic 不能为空 最大长度不超过127个字符 支持的符号 数字 字母 _ - | 不能包含其他字符
    Validators.checkMessage(msg, this.defaultMQProducer);
    final long invokeID = random.nextLong();
    
    long beginTimestampFirst = System.currentTimeMillis();
    long beginTimestampPrev = beginTimestampFirst;
    long endTimestamp = beginTimestampFirst;
    // 获取消息主题的路由信息 如果本地缓存没有 则从 NameServer 获取
    TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
    if (topicPublishInfo != null && topicPublishInfo.ok()) {
        boolean callTimeout = false;
        MessageQueue mq = null;
        Exception exception = null;
        SendResult sendResult = null;
        // 重试次数 同步发送默认3次 异步发送默认2次 
        int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
        int times = 0;
        String[] brokersSent = new String[timesTotal];
        boolean resetIndex = false;
        for (; times < timesTotal; times++) {
            String lastBrokerName = null == mq ? null : mq.getBrokerName();
            if (times > 0) {
                resetIndex = true;
            }
            //获取topic下的messagequeue 详细规则下面讲解
            MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName, resetIndex);
            if (mqSelected != null) {
                mq = mqSelected;
                brokersSent[times] = mq.getBrokerName();
                try {
                    beginTimestampPrev = System.currentTimeMillis();
                    if (times > 0) {
                        //Reset topic with namespace during resend.
                        msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));
                    }
                    long costTime = beginTimestampPrev - beginTimestampFirst;
                    if (timeout < costTime) {
                        callTimeout = true;
                        break;
                    }
                    // MQClientInstance 获取 MQClientAPIImpl 对象 通过网络模块发送消息 
                    sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
                    endTimestamp = System.currentTimeMillis();
                    
                    // 更新broker故障信息
                    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false, true);
                    switch (communicationMode) {
                        case ASYNC:
                            return null;
                        case ONEWAY:
                            return null;
                        case SYNC:
                            if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                                if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
                                    continue;
                                }
                            }

                            return sendResult;
                        default:
                            break;
                    }
                } catch (MQClientException e) {
                    endTimestamp = System.currentTimeMillis();
                    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false, true);
                    log.warn("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq, e);
                    log.warn(msg.toString());
                    exception = e;
                    continue;
                } catch (RemotingException e) {
                    endTimestamp = System.currentTimeMillis();
                    if (this.mqFaultStrategy.isStartDetectorEnable()) {
                        // Set this broker unreachable when detecting schedule task is running for RemotingException.
                        this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true, false);
                    } else {
                        // Otherwise, isolate this broker.
                        this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true, true);
                    }
                    log.warn("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq, e);
                    if (log.isDebugEnabled()) {
                        log.debug(msg.toString());
                    }
                    exception = e;
                    continue;
                } catch (MQBrokerException e) {
                    endTimestamp = System.currentTimeMillis();
                    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true, false);
                    log.warn("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq, e);
                    if (log.isDebugEnabled()) {
                        log.debug(msg.toString());
                    }
                    exception = e;
                    if (this.defaultMQProducer.getRetryResponseCodes().contains(e.getResponseCode())) {
                        continue;
                    } else {
                        if (sendResult != null) {
                            return sendResult;
                        }

                        throw e;
                    }
                } catch (InterruptedException e) {
                    endTimestamp = System.currentTimeMillis();
                    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false, true);
                    log.warn("sendKernelImpl exception, throw exception, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq, e);
                    if (log.isDebugEnabled()) {
                        log.debug(msg.toString());
                    }
                    throw e;
                }
            } else {
                break;
            }
        }

        if (sendResult != null) {
            return sendResult;
        }
        String info = String.format("Send [%d] times, still failed, cost [%d]ms, Topic: %s, BrokersSent: %s",
            times,
            System.currentTimeMillis() - beginTimestampFirst,
            msg.getTopic(),
            Arrays.toString(brokersSent));

        info += FAQUrl.suggestTodo(FAQUrl.SEND_MSG_FAILED);

        MQClientException mqClientException = new MQClientException(info, exception);
        if (callTimeout) {
            throw new RemotingTooMuchRequestException("sendDefaultImpl call timeout");
        }

        if (exception instanceof MQBrokerException) {
            mqClientException.setResponseCode(((MQBrokerException) exception).getResponseCode());
        } else if (exception instanceof RemotingConnectException) {
            mqClientException.setResponseCode(ClientErrorCode.CONNECT_BROKER_EXCEPTION);
        } else if (exception instanceof RemotingTimeoutException) {
            mqClientException.setResponseCode(ClientErrorCode.ACCESS_BROKER_TIMEOUT);
        } else if (exception instanceof MQClientException) {
            mqClientException.setResponseCode(ClientErrorCode.BROKER_NOT_EXIST_EXCEPTION);
        }

        throw mqClientException;
    }

    validateNameServerSetting();

    throw new MQClientException("No route info of this topic: " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO_TOPIC_ROUTE_INFO),
        null).setResponseCode(ClientErrorCode.NOT_FOUND_TOPIC_EXCEPTION);

获取topic下的messagequeue规则,selectOneMessageQueue分两种情况一种是开启故障延迟规避机制,一种是随机选择一个messagequeue;

public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName, final boolean resetIndex) {
    // 获取当前线程的BrokerFilter
    BrokerFilter brokerFilter = threadBrokerFilter.get();
    brokerFilter.setLastBrokerName(lastBrokerName);
    //sendLatencyFaultEnable配置的核心作用是实现消息发送端的故障延迟规避机制,
    // 通过动态隔离高延迟或不可用的 Broker 节点,提升生产者的整体可用性与系统稳定性。
    // 该机制通过实时监测 Broker 的响应状态,在特定时间段内自动规避异常节点,避免因单点故障或网络波动导致消息发送性能下降
    if (this.sendLatencyFaultEnable) {
        if (resetIndex) {
            tpInfo.resetIndex();
        }
        //availableFilter 作用 通过latencyFaultTolerance.isAvailable(mq.getBrokerName())判断broker是否可用
        MessageQueue mq = tpInfo.selectOneMessageQueue(availableFilter, brokerFilter);
        if (mq != null) {
            return mq;
        }

        mq = tpInfo.selectOneMessageQueue(reachableFilter, brokerFilter);
        if (mq != null) {
            return mq;
        }

        return tpInfo.selectOneMessageQueue();
    }
    //tpInfo.selectOneMessageQueue 作用是随机选择一个MessageQueue
    MessageQueue mq = tpInfo.selectOneMessageQueue(brokerFilter);
    if (mq != null) {
        return mq;
    }
    return tpInfo.selectOneMessageQueue();
}

rocketmq同步消息比较简单,接下来会再讲解broker收到消息的储存以及消息的网络模块;