同程旅行網基於 RocketMQ 高可用架構實踐

背景介紹

爲何選擇 RocketMQ

我們在幾年前決定引入 MQ 時,市場上已經有不少成熟的解決方案,比如 RabbitMQ , ActiveMQ,NSQ,Kafka 等。考慮到穩定性、維護成本、公司技術棧等因素,我們選擇了 RocketMQ :

使用情況

下圖是 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 集羣,跨雙機房,無論業務是單中心部署還是雙中心部署都不影響;而且只要升級客戶端即可,無需改動任何代碼。

雙中心訴求

就近原則

簡單說,就是確定兩件事:

如何判斷自己在哪個 idc?

1)  ip 查詢

節點啓動時可以獲取自身 ip ,通過公司內部的組件查詢所在的機房。

2)環境感知

需要與運維同學一起配合,在節點裝機時,將自身的一些元數據,比如機房信息等寫入本地配置文件,啓動時直接讀寫配置文件即可。

我們採用了第二個方案,無組件依賴,配置文件中 logicIdcUK 的值爲機房標誌。

客戶端節點如何識別在同一個機房的服務端節點?

客戶端節點可以拿到服務端節點的 ip 以及 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 主節點出現故障時,爲保障整個集羣的可用性,需要在 slave 中選主並切換。要做到這一點,首先得有個 broker 主故障的仲裁系統,即 nameserver(以下簡稱 ns )元數據系統(類似於 redis 中的哨兵)。

ns 元數據系統中的節點位於三個機房(有一個第三方的雲機房,在雲上部署 ns 節點,元數據量不大,延時可以接受),三個機房的 ns 節點通過 raft 協議選一個 leader,broker 節點會將元數據同步給 leader, leader 在將元數據同步給 follower 。

客戶端節點獲取元數據時, 從 leader,follower 中均可讀取數據。

切主流程

流程圖如下

切中心演練

用戶請求負載到雙中心,下面的操作先將流量切到二中心 --- 迴歸雙中心 --- 切到一中心。確保每個中心均可承擔全量用戶請求。

先將用戶流量全部切到二中心

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

回顧

MQ 平臺治理

即使系統高性能、高可用,倘若隨便使用或使用不規範,也會帶來各種各樣的問題,增加了不必要的維護成本,因此必要的治理手段不可或缺。

目的

治理哪些方面

主題 / 消費組治理

生產環境 MQ 集羣,我們關閉了自動創建主題與消費組,使用前需要先申請並記錄主題與消費組的項目標識與使用人。一旦出現問題,我們能夠立即找到主題與消費組的負責人,瞭解相關情況。若存在測試,灰度,生產等多套環境,可以一次申請多個集羣同時生效的方式,避免逐個集羣申請的麻煩。

爲避免業務疏忽發送大量無用的消息,有必要在服務端對主題生產速度進行流控,避免這個主題擠佔其他主題的處理資源。

對消息堆積敏感的消費組,使用方可設置消息堆積數量的閾值以及報警方式,超過這個閾值,立即通知使用方;亦可設置消息堆積時間的閾值,超過一段時間沒被消費,立即通知使用方。

消費節點下線或一段時間無響應,需要通知給使用方。

客戶端治理

監控發送 / 消費一條消息的耗時,檢測出性能過低的應用,通知使用方着手改造以提升性能;同時監控消息體大小,對消息體大小平均超過 10 KB 的項目,推動項目啓用壓縮或消息重構,將消息體控制在 10 KB 以內。

一條消息由哪個 ip 、在哪個時間點發送,又由哪些 ip 、在哪個時間點消費,再加上服務端統計的消息接收、消息推送的信息,構成了一條簡單的消息鏈路追蹤,將消息的生命週期串聯起來,使用方可通過查詢 msgId 或事先設置的 key 查看消息、排查問題。

隨着功能的不斷迭代,sdk 版本也會升級並可能引入風險。定時上報 sdk 版本,推動使用方升級有問題或過低的版本。

服務端治理

如何判斷一個集羣是健康的?定時檢測集羣中節點數量、集羣寫入 tps 、消費 tps ,並模擬用戶生產、消費消息。

性能指標最終反映在處理消息生產與消費的時間上。服務端統計處理每個生產、消費請求的時間,一個統計週期內,若存在一定比例的消息處理時間過長,則認爲這個節點性能有問題;引起性能問題的原因主要是系統物理瓶頸,比如磁盤 io util 使用率過高,cpu load 高等,這些硬件指標通過夜鷹監控系統自動報警。

高可用主要針對 broker 中 master 節點由於軟硬件故障無法正常工作,slave 節點自動被切換爲 master ,適合消息順序、集羣完整性有要求的場景。

部分後臺操作展示

主題與消費組申請

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

 

集羣監控

 

踩過的坑

社區對 MQ 系統經歷了長時間的改進與沉澱,我們在使用過程中也到過一些問題,要求我們能從深入瞭解源碼,做到出現問題心不慌,快速止損。

MQ 未來展望

目前消息保留時間較短,不方便對問題排查以及數據預測,我們接下來將對歷史消息進行歸檔以及基於此的數據預測。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/WKMaomohwWeglloKmmYkjg