RocketMQ原始碼解析之訊息消費者(consume Message)

$碼出未來發表於2020-12-03

原創不易,轉載請註明出處


前言

我們在《RocketMQ原始碼解析之訊息消費者(pullMessage)》一文中介紹了訊息消費者push模式拉取訊息的流程,訊息從broker拉取回來之後,將訊息放入對應的ProcessQueue的treeMap中,接著就是提交消費請求了,本文主要是介紹下RocketMQ提供的2種消費模式(併發消費,順序消費)的執行流程與原始碼剖析。

1.訊息流程介紹

RocketMQ支援併發消費與順序消費,這個採用順序消費還是併發消費,是與你採用實現的MessageListener(這個監聽器就是具體處理業務要實現的那個介面)型別有關,如果你實現的MessageListenerConcurrently這個介面的話,就是採用併發消費,如果是實現MessageListenerOrderly 這個介面的話,就是採用順序消費。
這裡先介紹下併發消費,併發消費是由ConsumeMessageConcurrentlyService 這個服務來處理的,然後當消費請求提交到這個服務的時候,它會根據你每次消費多少(預設是1),按照這個數量將訊息列表分成一個個的小集合,封裝消費請求,然後提交到執行緒池中。每個消費請求在執行的時候,會去呼叫你實現的MessageListener 來處理業務邏輯,然後處理消費結果,如果有失敗的,會將失敗訊息傳送給broker 的重試topic中,接著就是更新本地儲存消費offset中的值(這個消費offset 會在一定條件下 同步給broker,ProcessQueue移除的時候或者是定時任務,預設是5s觸發一次)。
我們在來介紹下這個順序消費,順序消費由ConsumeMessageOrderlyService 這個服務元件來處理,這個元件收到消費請求的時候,會封裝成一個ConsumeRequest,然後扔到執行緒池中,就一個ConsumeRequest,然後就是當執行緒迴圈獲取到的訊息集合,也是根據你每次消費多少,然後一批一批的去呼叫你實現的MessageListener 來處理業務邏輯,執行完這一批後處理執行結果,如果你處理業務邏輯的時候發生了異常或者執行結果返回null,這個時候就不會繼續消費了,會等待一會再消費,預設情況下是暫停1s,1s後再提交消費請求,如果正常消費,沒有出現異常啥的,就會更新消費offset,可以說這個順序消費能夠保證你這個消費是有序的,單執行緒,異常暫停,其實還有就是再rebalance的時候要向broker 申請鎖,也就是說只有一個消費者例項能夠消費對應的一個queue,消費之前要判斷這個鎖有沒有,然後有沒有失效。接下來我們分別看下併發消費與順序消費的實現原始碼是什麼樣子的

2.原始碼解析

這個要從DefaultMQPushConsumerImpl啟動的時候判斷監聽器MessageListener型別說起。
在這裡插入圖片描述
可以看到,不同型別實現的監聽器,對應的ConsumeMessageService也就不一樣,接下來在看下提交消費請求的程式碼
在這裡插入圖片描述
第一個引數就是 從broker拉取的訊息集合,然後第二個引數是ProcessQueue,第三個是對應的MessageQueue,第四個是要不要去消費

2.1 併發消費

先來看下併發消費的實現
在這裡插入圖片描述
上邊這一塊就是拉取回來的訊息個數正好在你批量消費範圍之內的話,就直接封裝ConsumeRequst,提交給執行緒池處理,如果是拉取回來的訊息個數大於你批量消費範圍的話,就分批封裝成ConsumeRequst,扔給執行緒池處理,預設批量消費是1個,如果你拉回來32個訊息,他就會給你封裝成ConsumeRequst物件,扔到執行緒池中併發消費, 注意這個執行緒池core是20 ,maxCore是60 ,然後佇列沒有限制大小。
接下來看下這個ConsumeRequst 任務的run方法實現
在這裡插入圖片描述
先是獲取你的messageListener ,然後執行前置hook,接著就是設定重試topic,然後迴圈給這些訊息新增消費時間,最後就是執行你messageListener中實現的consumeMessage 方法進行消費,這裡還沒有完,注意它的返回狀態status。
在這裡插入圖片描述
上面這一大塊其實就是處理這個status,也就是消費結果,如果你異常了,它這個status就是null,後面如果你是null的話,就設定RECONSUME_LATER,也就是稍後重新消費,接著就是執行消費後的鉤子,接著下面就是一堆記錄指標的東西了,如果processQueue沒有銷燬的話,就處理這個消費結果。我們來看下是怎樣處理消費結果的
在這裡插入圖片描述
這裡這一段就是計算成功幾個,然後失敗幾個的,這個ackIndex,你可以在消費的時候根據實際成功情況來確認ack,當然你也可以不用設定,只要你消費的時候異常不往上拋,它就認為你消費成功了,一旦讓它捕獲到異常,你這一批消費都要被放到重試佇列中。
在這裡插入圖片描述
這一塊就是根據這個ackIndex,往後的訊息它認為是失敗的,然後發給broker 的重試佇列中,進行重試消費,如果傳送重試訊息失敗就加到集合中,等一會在本地再消費一下。
最後就是在processQueue的treeMap中刪除消費完的訊息,獲取一個最小的offset,更新本地對應的消費offset。
在這裡插入圖片描述
這個就是將失敗的訊息傳送到broker的重試佇列中,可以看到有個延遲等級,這個其實就是根據你的重試次數進行延遲消費的,這裡預設的延遲等級是0,但是到broker端,它發現你傳過來的是0,它會給你設定成3+重試次數
在這裡插入圖片描述

2.2 順序消費

順序消費是在ConsumeMessageOrderlyService 這個服務元件中完成的,我們來看下
在這裡插入圖片描述
這裡就封裝一個消費請求,然後扔到執行緒池中,也就是從broker 拉回來的資料是順序執行的。
在這裡插入圖片描述
我們這個run方法比較長,但是大體思想就是獲取鎖,然後再做一些判斷,比如說順序消費的話有沒有從broker申請到鎖,申請的鎖有沒有過期,接著就是迴圈消費了,每次迴圈之前都要做一堆的判斷,再接著往下看
在這裡插入圖片描述
這裡上來先判斷了一下ConsumeRequest消耗的時間有沒有超過1分鐘,如果超過了就等會再消費,接著獲取每次消費的個數,從ProcessQueue中取出來對應個數的訊息,再往後就是執行消費前的hook
在這裡插入圖片描述
你會發現它每次消費前都要判斷一下有沒有drop,drop了就不要再消費了,drop出現在rebalance的時候,這次你這個消費者例項沒有分配到這個MessageQueue,或者這個MessageQueue好久沒有消費進行拉取訊息消費了,然後它就會給你drop,同時將本地儲存的offset 同步給broker ,接下來就是呼叫你實現的MessageListener 來處理你的業務邏輯進行消費,返回status,下面就需要關注status了
在這裡插入圖片描述
如果你處理業務異常了,或者返回了null,不管怎麼說只要status是null,就是暫停一小會,從字面意思上也能看出來,接著就是執行消費後的hook了,最後是處理消費結果,我們看下處理消費結果
在這裡插入圖片描述
先是判斷是否是自動提交,預設是的當然你可以改成非自動提交,如果成功的話,就獲取一下要提交的offset,如果是等一會的話,就等會提交消費請求,然後continueConsume=false,這個時候,前面的那個for迴圈就停了,就會等1s後再將消費請求提交,繼續消費,反正就是暫停1s。
在這裡插入圖片描述
前面那一大段程式碼就是手動提交的,其實都差不多,主要是看最後面這一行,如果沒有drop的話,就更新一下本地儲存消費offset,這個一般是存在記憶體裡,然後5s向broker 同步一下。
到這我們訊息消費者的併發消費與順序消費就介紹完了,並解析了對應的原始碼實現。

相關文章