RocketMQ源碼分析之生產者

RocketMQ源碼分析我們主要從NameSrv、路由、生產者、消費者、消息存儲等方面一點點分析,本章主要講的是生產者的源碼分析。

生產者方面的源碼主要分爲三個地方,啓動生產者、發送消息、批量發送消息,然後每個地方又會細分爲不同的小步驟,我會一點點分析。

一、前提

        使用過RocketMQ的都知道生產者類是DefaultMQProducer,該類在源碼的org.apache.rocketmq.client.producer下。通過源碼可以看到該類繼承了ClientConfig客戶端參數配置類同時實現了MQProducer接口,ClientConfig看名字大家都知道是幹什麼的了,這個就不細說了,至於MQProducer接口咱們繼續細看,MQProducer接口繼承了MQAdmin接口。

        所以從上到下的關係是DefaultMQProducer--》MQProducer --》MQAdmin。MQAdmin中的方法不多,就9個,都是最基礎的方法,創建topic、最大最小偏移量等,最主要的消息發送、客服端的啓動和註銷等方法其實在MQProducer接口中,然後DefaultMQProducer對這些方法進行具體實現。

        其實等你接下來細看的時候,會發現DefaultMQProducer方法的實現其實都是DefaultMQProducerImpl類做的具體實現,該類在org.apache.rocketmq.client.impl.producer包下。

 

 

二、啓動生產者

生產者的啓動在DefaultMQProducer的start()方法中實現的,具體看其實是調用DefaultMQProducerImpl的start()的方法。

在start()方法中又細分爲一下幾個步驟,接下來我會一個個詳細講解每一個步驟。 

1、參數校驗

其實主要是對生產者組進行校驗,組名的長度、組名的規則、組是否爲空等的校驗。

2、 將MQClient的名字改爲當前進程ID

3、創建一個MQClientMananger並通過該管理器創建一個MQClient

JVM中只有一個MQclientManager,通過該管理器創建不同的MQClient,同時維護一個MQClientInstance緩存表,該表中記錄了已經創建的MQClient。每次創建新的MQClient的時候,都會先創建一個clientId,然後根據clinetId去該表中查詢,如果存在則返回存在的,若不存在,則創建一個新的MQClient實例,然後將該實例信息存儲到MQClientInstance緩存表中。

 

4、將當前Producer註冊到MQClient中

 

5、啓動produer

啓動produer的時候,會啓動一堆東西,主要的啓動方法還是this.mQClientAPIImpl.start(),細看會發現其實是啓動一個remotingClient,然後remotingClient其實就是一個netty客戶端。

 

 

 

三、發送消息

消息的發送分爲很多種,異步發送、同步發送、oneway的方式等等,咱們就隨便分析其中一個同步發送消息,其實消息的發送也是用defaultMQProducerImpl做的的具體實現,發現消息前其實是先對消息做了校驗、然後就是查找topic對應路由的信息、選擇發送的對列、發送消息。

1、消息校驗

校驗消息不爲空,消息體不爲空,消息體的長度不爲空,然後消息的大小不能大於默認值4M。其實還有對topic的校驗,這裏就不細說了,可以自己進去看看。

2、路由的查找,查找topic對應路由的信息

跟創建MQClient一樣,路由相關的信息也會存在本地的一張緩存表,若能根據topic在緩存表中找到路由信息,則直接返回,若找不到則向namesrv請求去獲取該topic的路由信息 

 向namesrv獲取topic的路由信息時,若傳過來的參數isDefault是true則查詢默認的topic的路由信息,怎麼這肯定不是查找默認topic的信息,所以查找默認的咱們就不看了。其實查找topic的路由信息,還是通過一個nettyclient向namesrv發送查找路由的請求,然後namesrv返回。

 若topic的路由信息獲取到了,然後跟topic的路由表信息進行對比,如果兩者不一致,則更新路由表中該topic對應的路由信息。同時將獲取到的路由信息類topicRouteData轉換成需要的TopicPublishInfo類。

 

到此爲止,無論是從本地緩存表中獲取的還是向namesrv獲取的,topic對應路由的信息就拿到了。接下來就是選擇對列,每個broker上有不用的queue,需要選擇隊列,決定將消息發送到哪個或者哪些隊列。

3、選擇隊列

找到topic對應的路由信息以後,開始選擇隊列,將該topic的路由信息和接收最後一次消息的broker的名字傳過去,爲什麼要傳lastBrokerName?是因爲要做負載均衡,每次發送消息都發送到不同的broker上,保證消息不會都到一個broker上,達到負載均衡的作用,具體怎麼實現,我們繼續看。

selectOneMessageQueue()方法中有個判斷,判斷sendLatencyFaultEnable值爲true或者false,然後做不同的操作。sendLatencyFaultEnable的值默認爲false,該屬性是消息發送失敗延遲發送的開關,既然默認是false,咱們就先看爲false時候對於隊列的選取,然後再看爲true的情況。

如果沒有傳入 lastBrokerName,則進入selectOneMessageQueue()方法。

該方法中也就是,系統自動生成一個索引,然後該索引對消息隊列大小進行取餘,然後再根據取餘結果進行獲取消息隊列。

 再返回,如果lastBrokerName不爲null,則還是根據索引對消息隊列大小取餘,然後根據取餘結果獲取到消息隊列,如果該消息隊列所屬的broke跟傳過來的是同一個broker,則跳過重新獲取。若一直只有一個broker,那就沒辦法了,只能重新調用selectOneMessageQueue()方法,在該broker的隊列進行分配。

我們再返回到selectOneMessageQueue()中sendLatencyFaultEnable值爲true的情況,即設置了發送失敗延遲再發送的功能。

還是跟之前一樣,利用索引對消息隊列大小進行取餘,然後根據結果進行獲取消息隊列。唯一不同的是比較該消息隊列所屬的broker是否可用,producer本地會存儲消息發送失敗的broker的名字,然後跟它們進行對比,如果不存在其中,則代表該broker可用,將MessageQueue返回。

若選取的MessageQueue所屬的broker在那些失敗的broker之內,則放棄那個MessageQueue,從這些失敗的broker中挑出一個不是最好的broker,然後在topic的路由信息中獲取該broker的讀隊列,如果讀隊列大於0,則代表該broker的隊列處於空閒可被寫入。然後再在topic的路由信息中獲取一個MessageQueue,然後把這個MessageQueue進行填充爲那個不是不好的broker的隊列。

 

至此,消息隊列的選取已分析結束,接下來就是發送消息。 

4、發送消息

現在消息已經校驗、路由已經獲取、消息隊列已經確定,那麼就該發送消息了,我們就回到DefaultMQProducerImpl的

sendDefaultImpl消息發送方法了。發送消息其實是調用了DefaultMQProducerImpl的sendKernelImpl()方法。

 sendKernelImpl()方法中先是根據MessageQueue獲取broker,然後再獲取broker對應的brokerAddr,ip地址。

然後給message設置唯一id

 

接着就是判斷消息是否大於4k,如果大於4k,則設置一個壓縮標識,消息發送時根據該標識進行壓縮。 

 

 

 執行鉤子函數,如果之前給註冊了一個鉤子函數,做一些特殊的處理工作,那麼發送消息之前會將鉤子函數中的業務給執行了

由於是網絡通信,所以封裝消息頭,然後就是消息的發送了,根據第二步啓動創建的MQClient進行發送消息。 

 

 

四、批量發送消息

 批量發送消息可以將同一個topic下的多條消息一起發送,節省資源浪費,提高效率,但不是說該topic下所有的消息都合併成批量消息發送,消息的發送有限制的,之前說過4M,消息不是越多越好,而且大於4M就無法發送了。

其實批量消息的發送跟單條消息的發送就只有一個區別,批量是講多條消息合併,然後再按照單條消息發送的方式發送,因此,咱們只要看消息合併的那個方法即可,至於消息發送,跟單條的一樣。

還是DefaultMQProducer的send方法,不過是參數是個集合,多條消息的集合。

其實很簡單,就是將傳過來的消息集合轉換成MessageBatch,該類其實也是繼承Message。然後針對每條消息進行驗證,給消息設置唯一id,給消息設置topic,然後對批量消息類進行編碼,再給批量消息類設置topic,然後返回批量消息類MessageBatch。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章