RocketMQ 客戶端負載均衡機制詳解及最佳實踐
前言
Cloud Native
本文介紹 RocketMQ 負載均衡機制,主要涉及負載均衡發(fā)生的時機、客戶端負載均衡對消費的影響(消息堆積/消費毛刺等)并且給出一些最佳實踐的推薦。
負載均衡意義
Cloud Native
上圖是 RocketMQ 的消息儲存模型:消息是按照隊列的方式分區(qū)有序儲存的。RocketMQ 的隊列模型使得生產者、消費者和讀寫隊列都是多對多的映射關系,彼此之間都可以無限水平擴展。對比傳統(tǒng)的消息隊列如 RabbitMQ 是很大的優(yōu)勢。尤其是在流式處理場景下有天然優(yōu)勢,能夠保證同一隊列的消息被相同的消費者處理,對于批量處理、聚合處理更友好。
消費者消費某個 topic 的消息等同于消費這個 topic 上所有隊列的消息(上圖中 Consumer A1 消費隊列 1,Consumer A2 消費隊列 2、3)。
所以,要保證每個消費者的負載盡量均衡,也就是要給這些消費者分配相同數(shù)量的隊列,并保證在異常情況下(如客戶端宕機)隊列可以在不同消費者之間遷移。
負載均衡機制解析
Cloud Native
負載均衡時機
負載均衡是客戶端與服務端互相配合的過程,我們先綜合服務端和客戶端的職責回答第一個問題:何時會發(fā)生負載均衡。
客戶端主動負載均衡
上圖是 RocketMQ 客戶端相關類的結構,其中 MQClientInstance 負責和服務端的交互以及底層服務的協(xié)調,這其中就包括負載均衡。
MQClientInstance 中有兩個相關的方法 rebalanceImmediately 和 doRebalance,我們分析負載均衡的時機只要找到何時調用這兩個方法即可:
啟動時立即進行負載均衡;
定時(默認 20s)負載均衡一次。
服務端通知負載均衡
服務端通知客戶端進行負載均衡也是通過 MQClientInstance#rebalanceImmediately 方法實現(xiàn)的,我們同樣在服務端代碼中尋找相關調用。
分析以上幾個方法可以得出結論,在如下場景服務端會主動通知客戶端觸發(fā)負載均衡:
客戶端上下線
上線
新客戶端發(fā)送心跳到服務端
下線
客戶端發(fā)送下線請求到服務端
底層連接異常:響應 netty channel 的 IDLE/CLOSE/EXCEPTION 事件
2. 訂閱關系變化:訂閱新 topic 或有舊的 topic 不再訂閱
負載均衡策略
前文已經介紹了負載均衡實際是變更消費者負責處理的隊列數(shù)量,這里每次需要變更的隊列數(shù)量和受到影響的客戶端數(shù)量是由負載均衡策略決定的。
我們來分析一下比較常見的負載均衡策略:
平均分配
平均分配(AllocateMessageQueueAveragely)是默認的負載均衡策略:
如果我們有 4 個客戶端,24 個隊列,當?shù)诙䝼客戶端下線時:
以默認的負載均衡策略(AllocateMessageQueueAveragely)為例,重新分配隊列數(shù)量為 8。
默認的負載均衡策略能將隊列盡量均衡的分配到每個客戶端,但是每次負載均衡重新分配隊列數(shù)量較多,尤其是在客戶端數(shù)量很多的場景。
一致性哈希
基于一致性哈希算法的負載均衡策略(AllocateMessageQueueConsistentHash)每次負載均衡會重新分配盡可能少的隊列數(shù)量,但是可能會出現(xiàn)負載不均的情況。
負載均衡對消費的影響
Cloud Native
我們以一個真實的線上場景來舉例:
下圖中綠色的線代表發(fā)送 tps,黃色的線代表消費 tps,我們很容易發(fā)現(xiàn)在 21:00 和 21:50 分左右存在消費毛刺。
這兩個時間點在進行應用發(fā)布,根據我們上文的分析某個消費者下線后同組的其他消費者感知這一變化需要一定時間,導致有秒級的消費延遲產生。在發(fā)布結束后消費者快速處理堆積的消息,可以發(fā)現(xiàn)消費速度有一個明顯的上漲。
這個例子展示了下線時由于負載均衡帶來了短暫的消息處理延遲,新的消費者會從服務端獲取消費位點繼續(xù)之前的消費進度。如果消費者異常宕機或者沒有調用 shutdown 優(yōu)雅下線,沒有上傳自己的最新消費位點,會使得新分配的消費者重復消費。
這里我們總結下負載均衡對消費的影響,當某個客戶端觸發(fā)負載均衡時:
對于新分配的隊列可能會重復消費,這也是官方要求消費要做好冪等的原因;
對于不再負責的隊列會短時間消費停止,如果原本的消費 TPS 很高或者正好出現(xiàn)生產高峰就會造成消費毛刺。
最佳實踐
Cloud Native
避免頻繁上下線
為了避免負載均衡的影響應該盡量減少客戶端的上下線,同時做好消費冪等。
同時在有應用重啟或下線前要調用 shutdown 方法,這樣服務端在收到客戶端的下線請求后會通知客戶端及時觸發(fā)負載均衡,減少消費延遲。
選擇合適的負載均衡策略
需要根據業(yè)務需要靈活選擇負載均衡策略:
需要保證客戶端的負載盡可能的均衡:選擇默認的平均分配策略;
需要降低應用重啟帶來的消費延遲:選擇一致性哈希的分配策略。
當然還有其他負載均衡策略由于時間關系不一一介紹了,留給讀者自行探索。
RocketMQ 的負載均衡是每個客戶端獨立進行計算,所以務必要保證每個客戶端的負載均衡算法和訂閱語句一致。
負載均衡策略不一致會導致多個客戶端分配到相同隊列或有客戶端分不到隊列;
訂閱語句不一致會導致有消息未能消費。
RocketMQ 5.0 消息級別負載均衡
Cloud Native
為了徹底解決客戶端負載均衡導致的重復消費和消費延遲問題,RocketMQ 5.0 提出了消息級別的負載均衡機制。
同一個隊列的消息可以由多個消費者消費,服務端會確保消息不重不漏的被客戶端消費到:
消息粒度的負載均衡機制,是基于內部的單條消息確認語義實現(xiàn)的。消費者獲取某條消息后,服務端會將該消息加鎖,保證這條消息對其他消費者不可見,直到該消息消費成功或消費超時。因此,即使多個消費者同時消費同一隊列的消息,服務端也可保證消息不會被多個消費者重復消費。
在 4.x 的客戶端中,順序消費的實現(xiàn)強依賴于隊列的分配。RocketMQ 5.0 在消息維度的負載均衡的基礎上也實現(xiàn)了順序消費的語意:不同消費者處理同一個消息組內的消息時,會嚴格按照先后順序鎖定消息狀態(tài),確保同一消息組的消息串行消費。
如上圖所述,隊列 Queue1 中有 4 條順序消息,這 4 條消息屬于同一消息組 G1,存儲順序由 M1 到 M4。在消費過程中,前面的消息 M1、M2 被 消費者Consumer A1 處理時,只要消費狀態(tài)沒有提交,消費者 A2 是無法并行消費后續(xù)的 M3、M4 消息的,必須等前面的消息提交消費狀態(tài)后才能消費后面的消息。
文章來源:騰訊,如涉及到版權問題,請聯(lián)系網站管理員刪除!
- 上一篇:MySQL 設計數(shù)據表時,時間類型 datetime、big 2023/1/7
- 下一篇:PHP手動注入教程 2023/1/7
