RocketMQ DLedger架構在小米的大規模實踐

語言: CN / TW / HK

640.jpeg

本文作者:鄧志文,小米研發工程師,Apache RocketMQ Committer

小米訊息中介軟體選型

2.png

小米內部的業務場景可分為兩類,分別是資料業務和線上業務。

其中資料業務包括日誌流的計算、分析場景以及資料整合場景,一般使用內部自研的訊息佇列Talos。線上業務包括事件通知、訂單以及複雜的非同步呼叫場景,比如延時訊息、重試投遞、死信等,一般使用RocketMQ。

3.png

DLedger架構是RocketMQ 4.5 推出的全新架構,穩定性有保障。小米的線上核心業務規模巨大,需要很高的可靠性保證,因此我們最終選擇了DLedger架構。小米希望用資料說話,積極地擁抱社群發展,並且我們認為大規模落地DLedger既是挑戰,也是機會。

Dledger核心與優化

4.png

DLedger架構與主從架構的主要區別在於 broker。我們的實際部署中,單個Broker組部署3臺機器(建議部署奇數臺),主節點單獨部署,從節點與 NameServer 混合部署,通過 raid 方式將多塊磁碟組成單塊大盤。

5.png

DLedger架構有兩個核心模組:

①副本同步:主節點向從節點通過非同步傳送 push 請求。

②自動選主:主節點定時向從節點發送心跳,若從節點在指定時間內沒有收到心跳則觸發重新選主。

6.png

副本同步的過程如上圖所示:同步時 leader 節點會向從節點發送 push 請求,要求同步資料。

RocketMQ4.8 社群版本對DLedger做了以下兩個重要優化:

  • 非同步化 pipeline 模式:大幅提升了同步雙寫的效能。

  • 批量日誌複製:支援批量同步。但是批量同步會存在相容性問題,從單條升級到批量的情況下會出現不相容。

7.png

小米內部針對批量同步進行了優化,做了相容性改造:主從節點BatchPush相容性優化,避免叢集升級過程中叢集不可用。具體優化內容為:BatchPush相容性優化,支援了BatchPush滾動升級;以master節點配置為主,slave節點同時相容batch/single請求。

我們對 batch 同步做了效能測試,測試環境如上圖右側所示。測試結果為,開啟Batch同步前後,極限值從5.5w/s提高至8w/s,10ms內寫入極限從4.5w/s提升至7w/s。

8.png

DLedger選舉流程如上圖所示。通過配置preferredLeaderlds可以指定優先選舉某些節點為leader。由Candidate發起投票,獲取半數以上投票則成為主節點,否則重新發起投票。

9.png

故障情況下,故障恢復過程如上圖所示。

叢集必須保證一半以上節點可用。如果從節點出現故障,則業務無感知;如果主節點故障,則重新觸發選舉。主節點一旦發生故障,則會立即停止與從節點的心跳, 從節點會變為Candidate狀態,發起新一輪投票選舉,從剩餘存活的節點中選出新的 master 節點。

10.png

假設新的主節點並不是PreferredLeader,則會檢測Leader與PreferredLeader之間的水位差距。如果兩者之間副本寫入差距小於1000條,則會發起LeaderShipTransfer,將leader的位置轉移給PreferredLeader,此時Leader節點不再接收資料寫入。

Follower節點接收到LeaderShipTransfer請求後,將節點狀態設定為Candidate,不再接收副本同步。

當PreferredLeader被設定為Candidate時,節點副本同步進度將落後於Leader節點,會導致Candidate發起投票失敗。原因為副本同步落後,同時該節點term值大於leader節點,無法重新置為Follower,節點始終處於Candidate狀態。

我們對此進行了優化,在PreferredLeader節點接收到LeaderShipTransfer請求後,會跟上 leader 節點副本同步的進度,否則超時失敗,避免了被配置為 PreferredLeader的從節點資料不同步的問題。

DLedger架構實踐經驗

1.業務影響力

11.png

從2020年8月RocketMQ正式立項到現在,訊息規模已突破到 260 億條/天,預計2022-Q2可達 500 億/天。

12.png

截至目前,小米內部已將多種業務自維護中介軟體比如Notify、RabbitMQ等進行了替換。

2.效能優化

13.jpeg

小米內部的很多業務場景都需要延時訊息,然而,RocketMQ的延時訊息與訊息重試繫結,一旦客戶端出現大量消費失敗,會導致延時功能受到影響。

14.png

小米針對上述痛點進行了優化。

此前為一個執行緒處理18個level,level之間互相影響。因此,我們將Timer替換為ScheduledExecutorService,每一個執行緒負責一個level,使得level 之間不會互相影響。上圖右下角表格顯示,改造後的TPS依然較差,遠不能滿足業務團隊的需求。

15.png

於是我們繼續進行改造,通過非同步方式投遞訊息,由多個執行緒處理一個 level ,大幅提升了延時訊息的效能,比如邊讀邊寫的效能可達3.2w/s,相比於原先的700/s有了質的飛躍。

但是開啟非同步投遞後將無法保證延時訊息的順序性。

3.功能拓展

16.png

開源RocketMQ只有固定級別的延時訊息,無法滿足業務場景靈活延時訊息的需求。最終,我們通過外掛的方式實現了任意延遲訊息,方便相容社群後續的延時訊息,也可以輕鬆將其進行替換。

具體實現流程如下:所有延時訊息都發送到統一的延時 topic 裡,外掛將 topic 裡的訊息拉到自己的 CommitLog 中,再通過非同步執行緒將延遲訊息寫入 RocksDB。然後將延遲訊息從RocksDB載入到時間輪,由時間輪將到期的訊息投遞給業務 topic 。

Pull Service基於Push消費拉取訊息,可輕鬆對外掛進行水平擴充套件,consumer例項數增加時,能夠依賴 rebalance 特性實現自動負載均衡。另外,使用RocksDB做儲存,依賴kv特性無需對延時訊息做排序,降低了複雜度。延時訊息也是RocketMQ裡的一環,因此也需要很高的可靠性,所以我們基於DLedger實現了 3 副本,可靠性得到了很高的保證。

任意延遲訊息已經在內部所有叢集上線,業務規模龐大,效能和可靠性也得到了驗證。

17.png

RocketMQ5.0 以前,主要的消費模式為Pull和Push。而它們本質上都是 pull 的模式,客戶端需要與佇列數做繫結,一個分割槽最多隻能被一個客戶端消費,一旦消費者數量大於分割槽數量,則會導致空轉,有客戶端無法消費到訊息。同時,消費能力不同也會導致訊息堆積。此外,如果機器特別多比如有1000臺,則佇列的分割槽也必須為1000個。一旦有業務進行升級,則1000 臺機器都需要進行升級,耗時久,且升級時頻繁上下線會對消費造成很大影響。

RocketMQ5.0推出的POP消費模式能在一定程度上解決上述痛點。該模式下,佇列可被多個客戶端消費,客戶端無需進行 rebalance ,上下線也不會相互影響,消費更均衡。即使有1000 臺機器,可能只需要幾個分割槽、幾個佇列。

但RocketMQ5.0的POP模式依賴了內建的 level 延時,由於level不夠精確,因此在客戶端消費特別慢的場景下會出現消費重複的問題。此外,POP模式只能在PushConsumer裡使用。

因此,我們對RocketMQ5.0的PoP模式進行了優化,使用秒級延時訊息,並支援了PullConsumer場景。優化後的POP模式已在小米內部全叢集上線,POP消費數量超10億/日。

18.png

我們還基於Static Topic 實現了動態負載均衡。

叢集負載變高以後需要進行擴容,但是需要人工進行運維,手動地將流量高的 topic 從舊節點上遷移至新節點。而基於Static Topic實現了動態負載均衡後,新的節點加入後能夠自動將不同broker組上的流量進行均衡。

它包含兩個級別的均衡策略:

  • 磁碟均衡:按天級別統計 TPS和檢測負載。RocketMQ中無法刪除 topic,因此只要按TPS 統計,即可大概統計出不同節點磁碟的比例。

  • TPS均衡:按小時統計TPS和檢測負載。

19.png

如上圖,有四個 broker組,不同 broker組之間的流量閾值設定為 5k/s,一旦超過 5k/s則需要做rebalance。新建兩個 topic,其中topicA有3個佇列,topicB有5個佇列。

新建 topic 的分配策略為:先按TPS對Broker進行排序,再將Topic所有queue按照Broker順序迴圈分配。

最終結果如上圖:topicA的三個佇列分別分配在BrokerA、BrokerB、BrokerC,topicB的五個佇列分別分配在BrokerA(2個)、BrokerB(1個)、BrokerC(1個)、BrokerD(1個)。

20.png

隨著流量進入 topic ,勢必會造成broker組之間出現流量差距。如上圖,四個Broker的流量分別為13k、9k、9k、4k,已達 rebalance 閾值,因此需要進行 rebalance。

Balance 策略如下:

① 按照TPS對Topic進行由高到低的排序——排序結果為TopicB、TopicA。

② 按照TPS對Broker進行由低到高的排序——排序結果為BrokerD、BrokerA、BrokerB、BrokerC。

③ 按TPS由高到低對Topic/queue進行重新分配——首先分配TopicB,再分配TopicA。

21.png

④ 分配策略:按 TPS 由低到高對 Broker 迴圈分配,計算出每個 Broker 分配的 queue 個數——結果為BrokerA上的一個佇列移動至BrokerD。

⑤ 檢查 Broker 當前分配的 queue 個數,對 queue 進行遷移。

⑥ 按照上述邏輯迴圈,直到流量低於 Broker 閾值。

22.png

我們對監控、日誌、限流方面也分別進行了完善。

最開始我們使用小米內部的監控平臺進行監控和報警。如今,小米已將Prometheus+Grafana鏈路打通,有了更豐富的圖表元件,能夠提供更豐富的查詢和報警。

以往小米內部並沒有對日誌進行收集和監控,如果服務出現抖動,會在機器上檢視日誌。但是隨著機器的增加,檢視日誌的成本也逐漸變大。此外,日誌能夠提早暴露某些問題,因此我們將ElasticSearch+Grafana打通,既統一了日誌搜尋的入口,又增加了日誌的監控能力。

Broker雖然有自我保護,但僅針對叢集級別。很多業務會使用相同的叢集,為了保證不同叢集、不同業務之間互不干擾,我們增加了 topic 級別的限流能力,不會影響 topic 正常的業務邏輯,只會在異常情況下觸發限流,既保護了 broker也保護了其他業務。

4.災備

小米的很多業務對可靠性要求特別高,因此需要多機房做災備。

23.png

我們目前有三個災備方案:

  • 多機房多活。

  • 跨機房互備。

  • 雙機房熱備。

24.png

多機房多活:在多個機房部署Broker組,每個broker組部署在同一個機房。業務傳送流量時,三個 broker組都正常工作。其中一個 broker組出現問題後,通過負載均衡自動將流量導到另外兩個 broker組上。

該模式的缺陷為 broker 需要保留一定的 buffer 的,否則切流之後可能導致宕機。

25.png

跨機房互備:在多個機房部署broker組,每個broker組部署跨多個機房。有機房出現問題時,其他兩個節點依舊可用的,叢集也可用。客戶端無需進行任何改造,不需要自己實現流量均衡,且易於維護。

雙機房熱備屬於備選方案,單叢集三機房部署,雙機房使用Replicator同步。

未來規劃

26.png

小米對RocketMQ的未來規劃主要分為新特性落地和運維能力。

新特性方面,期望實現分級儲存、DLedger模式讀寫分離、以及批儲存&Client auto batch的落地應用。

運維能力方面,期望實現擴縮容自動化以提高運維能力,落地OpenTracing以實現對RocketMQ的全方位監控。

加入 Apache RocketMQ 社群

十年鑄劍,Apache RocketMQ 的成長離不開全球接近 500 位開發者的積極參與貢獻,相信在下個版本你就是 Apache RocketMQ 的貢獻者,在社群不僅可以結識社群大牛,提升技術水平,也可以提升個人影響力,促進自身成長。

社群 5.0 版本正在進行著如火如荼的開發,另外還有接近 30 個 SIG(興趣小組)等你加入,歡迎立志打造世界級分散式系統的同學加入社群,新增社群開發者微信:rocketmq666 即可進群,參與貢獻,打造下一代訊息、事件、流融合處理平臺。

27.jpeg

微信掃碼新增小火箭進群

另外還可以加入釘釘群與 RocketMQ 愛好者一起廣泛討論:

28.png

釘釘掃碼加群

關注「Apache RocketMQ」公眾號獲取更多技術乾貨