快手 RocketMQ 高效能實踐

語言: CN / TW / HK

1.png

本文作者:黃理,快手線上訊息系統負責人。

快手對於 RocketMQ 社群版本的優化一般為在其外層進行能力的構建,而不是對其內部進行大改動,因為內部大改不利於後期的版本升級。即使對內部 RocketMQ 進行了修改,我們也會盡量通過 PR 將新特性回饋到社群。

快手會定期對 RocketMQ 進行升級。2022年春節,我們大膽使用了尚未正式釋出的 RocketMQ 4.9.3-SNAPSHOT 版本,平穩度過了快手一年中最重要的活動,這也證實了社群版本 RocketMQ 的相容性和穩定性。

應用篇

2.png

RocketMQ 進入快手兩年內,從 0 發展到每天數千億訊息級別。快手是 RocketMQ 社群版本的事務訊息最早的大規模使用者,目前每天有百億以上的事務訊息和定時訊息,實現了跨 IDC的自動負載均衡以及容災,實現了多級泳道(專案)互不干擾,多個專案可同時開發,以及可回落。

3.png

RocketMQ的落地方式一般為兩種。

方式一:在開源軟體的基礎上進行修改,能夠快速輕鬆地實現需要的功能,但後續升級存在很大不便。

方式二:對RocketMQ的 client 和 server 只進行少量修改。如果 server 存在能力缺失,會開發輔助的 server 或以 proxy 進行補充。我們在 client 之上包裝了一層 MQ SDK,對使用者遮蔽了具體實現。快手也是基於此方式對RocketMQ進行了落地。

4.png

MQ SDK如上圖,分為三層:最上層為 API ,使用者與該層打交道;中間為核心層,能夠實現各種通用的能力;最底層負責與具體的 MQ 互動,當前只有 RocketMQ 的實現,但未來也許會有其他訊息中介軟體的實現。

中間層實現了熱變更的能力。使用者配置不寫在程式碼裡,而是在平臺進行指定,指定以後熱生效,SDK 會直接載入新的配置並自動 reload 使用者程式,無需重啟。

5.png

RocketMQ 會分配 Logic topic,業務程式碼無需關心叢集在哪,無需關心當前環境,也無需關心資料標記比如壓測、泳道,只需使用我們提供的非常簡單的 API ,複雜的過程全部在核心層進行封裝,對業務使用者遮蔽。

6.jpeg

上圖為跨機房負載均衡的實現流程。

所有Logic topic 都會對映到上圖中間的兩個機房,每個機房部署一個叢集。RocketMQ 叢集可以有多個 broker ,生產者和消費者對 broker 有故障感知和轉移能力,能夠通過 NameServer 發現哪個 broker 故障以避免與其進行連線。為了實現更好的控制,我們在叢集之上又封裝了一層負載均衡,並在 client 端實現。

每一個 Logic topic 都被分配到兩個叢集,生產者在生產時會同時連線到兩個叢集。兩個叢集分別位於不同 IDC,同一地區的兩個IDC之間延時大約為1-2ms。消費者側也是雙連,一旦某機房或叢集發生故障,流量會立刻自動轉移到另一機房。

我們將生產端的自動負載均衡(自動failover)進行了抽象封裝,做成了開源專案。Failover 元件與 RocketMQ 無關,在主調方做 RPC 或訊息生產等呼叫時,會檢查被調方的健康度。如果被調方不健康或響應很慢,則會調低其權重。

掃描上方二維碼,瞭解更多關於該元件的功能。

7.png

RocketMQ 實現了簡單的延遲訊息,但是隻能實現幾個固定級別的延時,而實際的業務更希望發訊息時可以任意指定延遲時間。因此我們通過外掛 Delay Server 的方式實現了任意精度的延遲訊息。

Delay Server 會將使用者要求延遲投遞的訊息進行儲存,等到指定時間以後,再將訊息送回原來的業務 topic 。

8.jpeg

基於PULL的模式(RocketMQ的PUSH Consumer也是基於PULL),很難直接實現動態的多泳道隔離。因此很多公司會選擇根據泳道的不同將訊息投遞到不同的 topic,這樣雖然實現了功能,但是侵入性太強,並且也不好實現多級回落。

而在快手,我們將所有泳道的消費都放在同一個 topic 裡,並且實現了多級泳道回落。比如專案 A 和專案 B 同時在開發,專案 A 生產的訊息由專案 A 的消費者消費。專案 A 下面有子專案A.X和A.Y,那麼A.X生產的訊息由A.X的消費者消費,但是如果A.Y沒有消費者,因此A.Y生產的訊息會回落到由 A 的消費者進行消費。

9.jpeg

快手的泳道隔離主要通過 RPC 轉發實現。

如上圖,黃色線代表專案 A 的資料流向,橙色線代表專案 B 的資料流向。SDK 會在將資料交給業務使用者之前,先檢查資料的泳道標記是否匹配,如果不匹配,則會通過 RPC 的方式轉發至匹配的消費者。

這樣我們等於是將 PULL 轉為了 PUSH,在主調方進行選擇,所以能夠將所有專案放在同一個 topic 裡,且能夠實現隔離。

10.png

RocketMQ 是基於 queue 來進行客戶端的 rebanance,如果消費者的數量大於queue,則會導致部分消費者無事可做;或有時 queue 分配不均勻,導致部分消費者負擔大、部分消費者負擔小。

我們通過RPC的方式實現了消費 Proxy,先從 RocketMQ 的 broker 消費資料,再通過 RPC 的方式  PUSH 到 consumer。

消費 proxy 的 RPC 與前文的 RPC 轉發使用同一套機制,因此RocketMQ 不需要過多的 queue, 但可以有很多消費者。消費 proxy 為可選項,只需在平臺指定消費方式,無需修改程式碼也無需重啟,即可直接熱生效。

11.jpeg

此外,我們開發了撥測程式,生成了一個模擬的生產者和消費者。生產者會往線上所有叢集的每一個 broker 傳送訊息,包括普通訊息、定時訊息和事務訊息。消費者消費到訊息後,會取出訊息體內生產者的 IP 地址,然後通過 TCP 的方式 ACK 回生產者,告知生產者訊息是否丟失、是否重複以及從生產到消費的延遲是多少。

另外,還可以針對生產者和消費者進行各種配置,比如訊息體大小、生產速率、對賬抽樣比例、對賬週期等。比如在配置中將速率調大,即可實現壓測,生產者和消費者會進行打點,最終可在 Grafana 上進行檢視和告警。

效能篇

對 RocketMQ 進行效能優化後,300位元組小訊息生產 TPS 提升54%,同時 CPU佔用降低11%;600 queue消費效能提升200%,跨IDC 100KB大訊息單執行緒生產 TPS 提升693%,跨IDC 100KB叢集消費吞吐提升250%。

優化主要分為兩批。

12.png

第一批優化集中在RocketMQ4.9.1版本,包括清除多餘的日誌、消除不必要的鎖、消除主從複製中的陣列拷貝、優化Broker的預設引數、優化鎖內操作的效能並將部分操作抽取到鎖外、優化訊息屬性編解碼的效能以及優化訊息Header解析的效能。掃描二維碼可閱讀本次優化相關的完整文章。

另外,RocketMQ 4.9.0版本的預設引數設定不合理,因此我們在4.9.1版本對其進行了優化,使得效能有了巨大提升。

13.png

上圖為第二批優化的目標:

  • 降低 CPU 開銷。RocketMQ 不消耗 CPU,但是在混合部署場景下,CPU 極有可能會成為問題,因此需要對 CPU 開銷進行優化。 

  • 提升跨機房的生產、消費的吞吐。

  • 提升大訊息的吞吐。

  • 提升queue特別多的場景下的消費效能。

第二批優化大部分集中在社群的ISSUE3585 裡,掃描上方二維碼可閱讀完整文章。

14.png

上圖為第二批優化CPU 方面的具體內容,字母編號與 ISSUE3585 相對應。此前RocketMQ 使用 Fastjson 做序列化,而有一個自定義的協議效能略微優於 Fastjson,我們對其進行了深入研究和優化,最終使得 RocketMQ 在編解碼上效能也得到了很大提升。

15.png

RocketMQ 構建 Queue 的程式為單執行緒,因此我們使用mmap buffer 代替 FileChannel,效能得到了極大提高。

上圖紅框中為某方法優化前後對比。

16.png

此外,我們將 Pull 通知移動到另外的執行緒,使其不佔用執行緒的資源。同時,將通知自動聚批,兼顧高吞吐和低延遲,只有 TPS 較高時才會自動開啟,閾值可設定,避免 Pull 通知在 Broker 和消費者之間震盪頻率過高,消耗 CPU。

17.png

大訊息效能不佳也是一直以來很大的困擾,經過分析我們發現 RocketMQ remoting 的 TCP 引數設定有待優化。原先的buffer 設定為固定值,當機房延遲很大時,過小的 buffer 會導致 TCP 連線的吞吐受到嚴重影響。因此,我們將 buffer 修改為由作業系統自動管理,以保證吞吐。

上圖列出了線上實測的資料表,可以看出消費的效能提升了數倍。

18.png

在極高的 TPS 場景下,如果訊息體較大,則作業系統記憶體分配可能會成為 RocketMQ 的瓶頸,4.X 的核心下的表現遠遠優於 3 .X 。極端場景下,3.X核心通常需要將引數 min_free_kbytes 調至較大值。

19.png

我們於2021年12月社群開發者會議現場演示了效能測試,對比了第二批優化前後的4.9.2版本和4.9.3 Snapshot版本(包括當時還未合併進該版本的B和K的優化)的效能。

測試時,在電腦上同時執行所有 broker、生產者、消費者。老版本TPS不到 3w,而新版本有了自動聚批的能力,TPS 可達 6 w,相比於老版本提升了一倍。

加入 Apache RocketMQ 社群

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

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

20.jpeg

微信掃碼新增小火箭進群

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

21.png

釘釘掃碼加群

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