RocketMQ - 生產者訊息傳送流程

VipSoft發表於2023-02-21

RocketMQ客戶端的訊息傳送通常分為以下3層

  • 業務層:通常指直接呼叫RocketMQ Client傳送API的業務程式碼。
  • 訊息處理層:指RocketMQ Client獲取業務傳送的訊息物件後,一系列的引數檢查、訊息傳送準備、引數包裝等操作。
  • 通訊層:指RocketMQ基於Netty封裝的一個RPC通訊服務,RocketMQ的各個元件之間的通訊全部使用該通訊層。

訊息傳送流程首先是RocketMQ客戶端接收業務層訊息,然後透過DefaultMQProducerImpl傳送一個RPC請求給Broker,再由Broker處理請求並儲存訊息。
image

下面以DefaultMQProducer.send(Messagemsg)介面為例講解傳送流程,

  • 第一步:呼叫defaultMQProducerImpl.send()方法傳送訊息。
  • 第二步:透過設定的傳送超時時間,呼叫defaultMQProducerImpl.send()方法傳送訊息。設定的超時時間可以透過sendMsgTimeout進行變更,其預設值為3s。
  • 第三步:執行 defaultMQProducerImpl.sendDefaultImpl()方法。
private SendResult sendDefaultImpl(
    Message msg,
    //通訊模式,同步、非同步還是單向
    final CommunicationMode communicationMode,
    //對於非同步模式,需要設定傳送完成後的回撥
    final SendCallback sendCallback,
    final long timeout
){}

該方法是傳送訊息的核心方法,執行過程分為5步:
第一步: 兩個檢查:生產者狀態、訊息及訊息內容。沒有執行的生產者不能傳送訊息。訊息檢查主要檢查訊息是否為空,訊息的Topic的名字是否為空或者是否符合規範;訊息體大小是否符合要求,最大值為4MB,可以透過maxMessageSize進行設定。
第二步: 執行tryToFindTopicPublishInfo()方法:獲取Topic路由資訊,如果不存在則發出異常提醒使用者。如果本地快取沒有路由資訊,就透過Namesrv獲取路由資訊,更新到本地,再返回。具體實現程式碼如下:

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;
    }
}

第三步: 計算訊息傳送的重試次數,同步重試和非同步重試的執行方式是不同的。
第四步: 執行佇列選擇方法selectOneMessageQueue()。根據佇列物件中儲存的上次傳送訊息的Broker的名字和Topic路由,選擇(輪詢)一個Queue將訊息傳送到 Broker 。 我們可以透過sendLatencyFaultEnable 來設定是否總是傳送到延遲級別較低的 Broker,預設值為False。
第五步: 執行sendKernelImpl()方法。該方法是傳送訊息的核心方法,主要用於準備通訊層的入參(比如Broker地址、請求體等),將請求傳遞給通訊層,內部實現是基於Netty的,在封裝為通訊層request物件RemotingCommand前,會設定RequestCode表示當前請求是傳送單個訊息還是批次訊息。

相關文章