
背景介紹
為何選擇 RocketMQ
我們在幾年前決定引入 MQ 時,市場上已經有不少成熟的解決方案,比如 RabbitMQ , ActiveMQ,NSQ,Kafka 等。考慮到穩定性、維護成本、公司技術棧等因素,我們選擇了 RocketMQ :
- 純 Java 開發,無依賴,使用簡單,出現問題能 hold ;
- 經過阿裡雙十一考驗,性能、穩定性可以保障;
- 功能實用,發送端:同步、異步、單邊、延時發送;消費端:消息重置,重試隊列,死信隊列;
- 社群活躍,出問題能及時溝通解決。
使用情況
- 主要用于削峰、解耦、異步處理;
- 已在火車票、機票、酒店等核心業務廣泛使用,扛住巨大的微信入口流量;
- 在支付、訂單、出票、資料同步等核心流程廣泛使用;
- 每天 1000+ 億條消息周轉。
下圖是 MQ 接入架構圖
由于公司技術棧原因,client sdk 我們提供了 java sdk ;對于其他語言,收斂到 http proxy ,屏蔽語言細節,節約維護成本。按照各大業務線,對後端存儲節點進行了隔離,互相不影響。

MQ 雙中心改造
之前單機房出現過網絡故障,對業務影響較大。為保障業務高可用,同城雙中心改造提上了日程。
為何做雙中心
- 單機房故障業務可用;
- 保證資料可靠:若所有資料都在一個機房,一旦機房故障,資料有丢失風險;
- 橫向擴容:單機房容量有限,多機房可分擔流量。
雙中心方案
做雙中心之前,對同城雙中心方案作了些調研,主要有冷(熱)備份、雙活兩種。(當時社群 Dledger 版本還沒出現,Dledger 版本完全可做為雙中心的一種可選方案。)
1)同城冷(熱)備份
兩個獨立的 MQ 叢集, 使用者流量寫到一個主叢集,資料實時同步到備用叢集,社群有成熟的 RocketMQ Replicator 方案,需要定期同步中繼資料,比如主題,消費組,消費進度等。

2)同城雙活
兩個獨立 MQ 叢集,使用者流量寫到各自機房的 MQ 叢集,資料互相不同步。
平時業務寫入各自機房的 MQ 叢集,若一個機房挂了,可以将使用者請求流量全部切到另一個機房,消息也會生産到另一個機房。
對于雙活方案,需要解決 MQ 叢集域名。
1)若兩個叢集用一個域名,域名可以動态解析到各自機房。此方式要求生産、消費必須在同一個機房。假如生産在 idc1 ,消費在 idc2 ,這樣生産、消費各自連接配接一個叢集,沒法消費資料。
2)若一個叢集一個域名,業務方改動較大,我們之前對外服務的叢集是單中心部署的,業務方已經大量接入,此方案推廣較困難。
為盡可能減少業務方改動,域名隻能繼續使用之前的域名,最終我們采用一個 Global MQ 叢集,跨雙機房,無論業務是單中心部署還是雙中心部署都不影響;而且隻要更新用戶端即可,無需改動任何代碼。
雙中心訴求
- 就近原則:生産者在 A 機房,生産的消息存于 A 機房 broker ; 消費者在 A 機房,消費的消息來自 A 機房 broker 。
- 單機房故障:生産正常,消息不丢。
- broker 主節點故障:自動選主。
就近原則
簡單說,就是确定兩件事:
- 節點(用戶端節點,服務端節點)如何判斷自己在哪個 idc;
- 用戶端節點如何判斷服務端節點在哪個 idc。
如何判斷自己在哪個 idc?
1) ip 查詢
節點啟動時可以擷取自身 ip ,通過公司内部的元件查詢所在的機房。
2)環境感覺
需要與運維同學一起配合,在節點裝機時,将自身的一些中繼資料,比如機房資訊等寫入本地配置檔案,啟動時直接讀寫配置檔案即可。
我們采用了第二個方案,無元件依賴,配置檔案中 logicIdcUK 的值為機房标志。

用戶端節點如何識别在同一個機房的服務端節點?
用戶端節點可以拿到服務端節點的 ip 以及 broker 名稱的,是以:
- ip 查詢:通過公司内部元件查詢 ip 所在機房資訊;
- broker 名稱增加機房資訊:在配置檔案中,将機房資訊添加到 broker 名稱上;
- 協定層增加機房辨別:服務端節點向中繼資料系統注冊時,将自身的機房資訊一起注冊。
相對于前兩者,實作起來略複雜,改動了協定層, 我們采用了第二種與第三種結合的方式。
就近生産
基于上述分析,就近生産思路很清晰,預設優先本機房就近生産;
若本機房的服務節點不可用,可以嘗試擴機房生産,業務可以根據實際需要具體配置。

就近消費
優先本機房消費,預設情況下又要保證所有消息能被消費。
隊列配置設定算法采用按機房配置設定隊列
- 每個機房消息平均分給此機房消費端;
- 此機房沒消費端,平分給其他機房消費端。
僞代碼如下:
Map<String, Set> mqs = classifyMQByIdc(mqAll);
Map<String, Set> cids = classifyCidByIdc(cidAll);
Set<> result = new HashSet<>;
for(element in mqs){
result.add(allocateMQAveragely(element, cids, cid)); //cid為目前用戶端
}
消費場景主要是消費端單邊部署與雙邊部署。
單邊部署時,消費端預設會拉取每個機房的所有消息。

雙邊部署時,消費端隻會消費自己所在機房的消息,要注意每個機房的實際生産量與消費端的數量,防止出現某一個機房消費端過少。

單機房故障
- 每組 broker 配置
一主兩從,一主一從在一機房,一從在另一機房;某一從同步完消息,消息即發送成功。
消息生産跨機房;未消費消息在另一機房繼續被消費。
故障切主
在某一組 broker 主節點出現故障時,為保障整個叢集的可用性,需要在 slave 中選主并切換。要做到這一點,首先得有個broker 主故障的仲裁系統,即 nameserver(以下簡稱 ns )中繼資料系統(類似于 redis 中的哨兵)。
ns 中繼資料系統中的節點位于三個機房(有一個第三方的雲機房,在雲上部署 ns 節點,中繼資料量不大,延時可以接受),三個機房的 ns 節點通過 raft 協定選一個leader,broker 節點會将中繼資料同步給 leader, leader 在将中繼資料同步給 follower 。
用戶端節點擷取中繼資料時, 從 leader,follower 中均可讀取資料。

切主流程
- 若 nameserver leader 監控到 broker 主節點異常, 并要求其他 follower 确認;半數 follower 認為 broker 節點異常,則 leader 通知在 broker 從節點中選主,同步進度大的從節點選為主;
- 新選舉的 broker 主節點執行切換動作并注冊到中繼資料系統;
- 生産端無法向舊 broker 主節點發送消息。
流程圖如下

切中心演練
使用者請求負載到雙中心,下面的操作先将流量切到二中心---回歸雙中心---切到一中心。確定每個中心均可承擔全量使用者請求。
先将使用者流量全部切到二中心

流量回歸雙中心,并切到一中心

回顧
- 全局 Global 叢集
- 一主二從,寫過半消息即及寫入成功
- 中繼資料系統 raft 選主
- broker 主節點故障,自動選主
MQ 平台治理
即使系統高性能、高可用,倘若随便使用或使用不規範,也會帶來各種各樣的問題,增加了不必要的維護成本,是以必要的治理手段不可或缺。
目的
讓系統更穩定
- 及時告警
- 快速定位、止損
治理哪些方面
主題/消費組治理
- 申請使用
生産環境 MQ 叢集,我們關閉了自動建立主題與消費組,使用前需要先申請并記錄主題與消費組的項目辨別與使用人。一旦出現問題,我們能夠立即找到主題與消費組的負責人,了解相關情況。若存在測試,灰階,生産等多套環境,可以一次申請多個叢集同時生效的方式,避免逐個叢集申請的麻煩。
- 生産速度
為避免業務疏忽發送大量無用的消息,有必要在服務端對主題生産速度進行流控,避免這個主題擠占其他主題的處理資源。
- 消息積壓
對消息堆積敏感的消費組,使用方可設定消息堆積數量的門檻值以及報警方式,超過這個門檻值,立即通知使用方;亦可設定消息堆積時間的門檻值,超過一段時間沒被消費,立即通知使用方。
- 消費節點掉線
消費節點下線或一段時間無響應,需要通知給使用方。
用戶端治理
- 發送、消費耗時檢測
監控發送/消費一條消息的耗時,檢測出性能過低的應用,通知使用方着手改造以提升性能;同時監控消息體大小,對消息體大小平均超過 10 KB 的項目,推動項目啟用壓縮或消息重構,将消息體控制在 10 KB 以内。
- 消息鍊路追蹤
一條消息由哪個 ip 、在哪個時間點發送,又由哪些 ip 、在哪個時間點消費,再加上服務端統計的消息接收、消息推送的資訊,構成了一條簡單的消息鍊路追蹤,将消息的生命周期串聯起來,使用方可通過查詢msgId或事先設定的 key 檢視消息、排查問題。
- 過低或有隐患版本檢測
随着功能的不斷疊代,sdk 版本也會更新并可能引入風險。定時上報 sdk 版本,推動使用方更新有問題或過低的版本。
服務端治理
- 叢集健康巡檢
如何判斷一個叢集是健康的?定時檢測叢集中節點數量、叢集寫入 tps 、消費 tps ,并模拟使用者生産、消費消息。
- 叢集性能巡檢
性能名額最終反映在處理消息生産與消費的時間上。服務端統計處理每個生産、消費請求的時間,一個統計周期内,若存在一定比例的消息處理時間過長,則認為這個節點性能有問題;引起性能問題的原因主要是系統實體瓶頸,比如磁盤 io util 使用率過高,cpu load 高等,這些硬體名額通過夜鷹監控系統自動報警。
- 叢集高可用
高可用主要針對 broker 中 master 節點由于軟硬體故障無法正常工作,slave 節點自動被切換為 master ,适合消息順序、叢集完整性有要求的場景。
部分背景操作展示
主題與消費組申請

生産,消費,堆積實時統計

叢集監控

踩過的坑
社群對 MQ 系統經曆了長時間的改進與沉澱,我們在使用過程中也到過一些問題,要求我們能從深入了解源碼,做到出現問題心不慌,快速止損。
- 新老消費端并存時,我們實作的隊列配置設定算法不相容,做到相容即可;
- 主題、消費組數量多,注冊耗時過長,記憶體 oom ,通過壓縮縮短注冊時間,社群已修複;
- topic 長度判斷不一緻,導緻重新開機丢消息,社群已修複;
- centos 6.6 版本中,broker 程序假死,更新 os 版本即可。
MQ 未來展望
目前消息保留時間較短,不友善對問題排查以及資料預測,我們接下來将對曆史消息進行歸檔以及基于此的資料預測。
- 曆史資料歸檔
- 底層存儲剝離,計算與存儲分離
- 基于曆史資料,完成更多資料預測
- 服務端更新到 Dledger ,確定消息的嚴格一緻
了解更多 RocketMQ 資訊,可加入社群交流群,下面是釘釘群,歡迎大家加群留言。
