同程旅行基于 RocketMQ 高可用架构实践
背景介紹
?
為何選擇 RocketMQ
?
我們在幾年前決定引入 MQ 時,市場上已經(jīng)有不少成熟的解決方案,比如 RabbitMQ , ActiveMQ,NSQ,Kafka 等。考慮到穩(wěn)定性、維護成本、公司技術(shù)棧等因素,我們選擇了 RocketMQ :
?
- 純 Java 開發(fā),無依賴,使用簡單,出現(xiàn)問題能 hold ;
- 經(jīng)過阿里雙十一考驗,性能、穩(wěn)定性可以保障;
- 功能實用,發(fā)送端:同步、異步、單邊、延時發(fā)送;消費端:消息重置,重試隊列,死信隊列;
- 社區(qū)活躍,出問題能及時溝通解決。
?
使用情況
?
- 主要用于削峰、解耦、異步處理;
- 已在火車票、機票、酒店等核心業(yè)務(wù)廣泛使用,扛住巨大的微信入口流量;
- 在支付、訂單、出票、數(shù)據(jù)同步等核心流程廣泛使用;
- 每天 1000+ 億條消息周轉(zhuǎn)。
?
下圖是 MQ 接入框架圖
?
由于公司技術(shù)棧原因,client sdk 我們提供了 java sdk ;對于其他語言,收斂到 http proxy ,屏蔽語言細節(jié),節(jié)約維護成本。按照各大業(yè)務(wù)線,對后端存儲節(jié)點進行了隔離,相互不影響。
?
MQ 雙中心改造
?
之前單機房出現(xiàn)過網(wǎng)絡(luò)故障,對業(yè)務(wù)影響較大。為保障業(yè)務(wù)高可用,同城雙中心改造提上了日程。
?
為何做雙中心
?
- 單機房故障業(yè)務(wù)可用;?
- 保證數(shù)據(jù)可靠:若所有數(shù)據(jù)都在一個機房,一旦機房故障,數(shù)據(jù)有丟失風(fēng)險;
- 橫向擴容:單機房容量有限,多機房可分擔(dān)流量。
?
雙中心方案
?
做雙中心之前,對同城雙中心方案作了些調(diào)研,主要有冷(熱)備份、雙活兩種。(當(dāng)時社區(qū) Dledger 版本還沒出現(xiàn),Dledger 版本完全可做為雙中心的一種可選方案。)
?
1)同城冷(熱)備份
?
兩個獨立的 MQ 集群, 用戶流量寫到一個主集群,數(shù)據(jù)實時同步到備用集群,社區(qū)有成熟的 RocketMQ Replicator 方案,需要定期同步元數(shù)據(jù),比如主題,消費組,消費進度等。
?
2)同城雙活
?
兩個獨立 MQ 集群,用戶流量寫到各自機房的 MQ 集群,數(shù)據(jù)相互不同步。
?
平時業(yè)務(wù)寫入各自機房的 MQ 集群,若一個機房掛了,可以將用戶請求流量全部切到另一個機房,消息也會生產(chǎn)到另一個機房。
?
對于雙活方案,需要解決 MQ 集群域名。
1)若兩個集群用一個域名,域名可以動態(tài)解析到各自機房。此方式要求生產(chǎn)、消費必須在同一個機房。假如生產(chǎn)在 idc1 ,消費在 idc2 ,這樣生產(chǎn)、消費各自連接一個集群,沒法消費數(shù)據(jù)。
2)若一個集群一個域名,業(yè)務(wù)方改動較大,我們之前對外服務(wù)的集群是單中心部署的,業(yè)務(wù)方已經(jīng)大量接入,此方案推廣較困難。
為盡可能減少業(yè)務(wù)方改動,域名只能繼續(xù)使用之前的域名,最終我們采用一個 Global MQ 集群,跨雙機房,無論業(yè)務(wù)是單中心部署還是雙中心部署都不影響;而且只要升級客戶端即可,無需改動任何代碼。
雙中心訴求
?
- 就近原則:生產(chǎn)者在 A 機房,生產(chǎn)的消息存于 A 機房 broker ; 消費者在 A 機房,消費的消息來自 A 機房 broker 。
- 單機房故障:生產(chǎn)正常,消息不丟。
- broker 主節(jié)點故障:自動選主。
就近原則
?
簡單說,就是確定兩件事:
- 節(jié)點(客戶端節(jié)點,服務(wù)端節(jié)點)如何判斷自己在哪個 idc;
- 客戶端節(jié)點如何判斷服務(wù)端節(jié)點在哪個 idc。
如何判斷自己在哪個 idc?
?
節(jié)點啟動時可以獲取自身 ip ,通過公司內(nèi)部的組件查詢所在的機房。
?
2)環(huán)境感知
需要與運維同學(xué)一起配合,在節(jié)點裝機時,將自身的一些元數(shù)據(jù),比如機房信息等寫入本地配置文件,啟動時直接讀寫配置文件即可。
我們采用了第二個方案,無組件依賴,配置文件中 logicIdcUK 的值為機房標(biāo)志。
?
客戶端節(jié)點如何識別在同一個機房的服務(wù)端節(jié)點?
?
客戶端節(jié)點可以拿到服務(wù)端節(jié)點的 ip 以及 broker 名稱的,因此:
?
- ip 查詢:通過公司內(nèi)部組件查詢 ip 所在機房信息;
- broker 名稱增加機房信息:在配置文件中,將機房信息添加到 broker 名稱上;
- 協(xié)議層增加機房標(biāo)識:服務(wù)端節(jié)點向元數(shù)據(jù)系統(tǒng)注冊時,將自身的機房信息一起注冊。
相對于前兩者,實現(xiàn)起來略復(fù)雜,改動了協(xié)議層, 我們采用了第二種與第三種結(jié)合的方式。
就近生產(chǎn)
?
基于上述分析,就近生產(chǎn)思路很清晰,默認優(yōu)先本機房就近生產(chǎn);
?
若本機房的服務(wù)節(jié)點不可用,可以嘗試擴機房生產(chǎn),業(yè)務(wù)可以根據(jù)實際需要具體配置。
就近消費
?
優(yōu)先本機房消費,默認情況下又要保證所有消息能被消費。
?
隊列分配算法采用按機房分配隊列
?
- 每個機房消息平均分給此機房消費端;
- 此機房沒消費端,平分給其他機房消費端。
?
偽代碼如下:
?
?
消費場景主要是消費端單邊部署與雙邊部署。
?
單邊部署時,消費端默認會拉取每個機房的所有消息。
雙邊部署時,消費端只會消費自己所在機房的消息,要注意每個機房的實際生產(chǎn)量與消費端的數(shù)量,防止出現(xiàn)某一個機房消費端過少。
單機房故障
?
- 每組 broker 配置
一主兩從,一主一從在一機房,一從在另一機房;某一從同步完消息,消息即發(fā)送成功。
?
- 單機房故障
消息生產(chǎn)跨機房;未消費消息在另一機房繼續(xù)被消費。
故障切主
?
在某一組 broker 主節(jié)點出現(xiàn)故障時,為保障整個集群的可用性,需要在 slave 中選主并切換。要做到這一點,首先得有個broker 主故障的仲裁系統(tǒng),即 nameserver(以下簡稱 ns )元數(shù)據(jù)系統(tǒng)(類似于 redis 中的哨兵)。
?
ns 元數(shù)據(jù)系統(tǒng)中的節(jié)點位于三個機房(有一個第三方的云機房,在云上部署 ns 節(jié)點,元數(shù)據(jù)量不大,延時可以接受),三個機房的 ns 節(jié)點通過 raft 協(xié)議選一個leader,broker 節(jié)點會將元數(shù)據(jù)同步給 leader, leader 在將元數(shù)據(jù)同步給 follower 。
?
客戶端節(jié)點獲取元數(shù)據(jù)時, 從 leader,follower 中均可讀取數(shù)據(jù)。
?
切主流程
?
- 若 nameserver leader 監(jiān)控到 broker 主節(jié)點異常, 并要求其他 follower 確認;半數(shù) follower 認為 broker 節(jié)點異常,則 leader 通知在 broker 從節(jié)點中選主,同步進度大的從節(jié)點選為主;
- 新選舉的 broker 主節(jié)點執(zhí)行切換動作并注冊到元數(shù)據(jù)系統(tǒng);
- 生產(chǎn)端無法向舊 broker 主節(jié)點發(fā)送消息。
?
流程圖如下
切中心演練
?
用戶請求負載到雙中心,下面的操作先將流量切到二中心—回歸雙中心—切到一中心。確保每個中心均可承擔(dān)全量用戶請求。
先將用戶流量全部切到二中心
流量回歸雙中心,并切到一中心
?
回顧
- 全局 Global 集群
- 就近原則
- 一主二從,寫過半消息即及寫入成功
- 元數(shù)據(jù)系統(tǒng) raft 選主
- broker 主節(jié)點故障,自動選主
?
MQ 平臺治理
?
即使系統(tǒng)高性能、高可用,倘若隨便使用或使用不規(guī)范,也會帶來各種各樣的問題,增加了不必要的維護成本,因此必要的治理手段不可或缺。
?
目的
?讓系統(tǒng)更穩(wěn)定
- 及時告警
- 快速定位、止損
?
治理哪些方面
?
主題/消費組治理
?
- 申請使用
生產(chǎn)環(huán)境 MQ 集群,我們關(guān)閉了自動創(chuàng)建主題與消費組,使用前需要先申請并記錄主題與消費組的項目標(biāo)識與使用人。一旦出現(xiàn)問題,我們能夠立即找到主題與消費組的負責(zé)人,了解相關(guān)情況。若存在測試,灰度,生產(chǎn)等多套環(huán)境,可以一次申請多個集群同時生效的方式,避免逐個集群申請的麻煩。
?
- 生產(chǎn)速度
為避免業(yè)務(wù)疏忽發(fā)送大量無用的消息,有必要在服務(wù)端對主題生產(chǎn)速度進行流控,避免這個主題擠占其他主題的處理資源。
?
- 消息積壓
對消息堆積敏感的消費組,使用方可設(shè)置消息堆積數(shù)量的閾值以及報警方式,超過這個閾值,立即通知使用方;亦可設(shè)置消息堆積時間的閾值,超過一段時間沒被消費,立即通知使用方。
?
- 消費節(jié)點掉線
消費節(jié)點下線或一段時間無響應(yīng),需要通知給使用方。
客戶端治理
?
- 發(fā)送、消費耗時檢測
監(jiān)控發(fā)送/消費一條消息的耗時,檢測出性能過低的應(yīng)用,通知使用方著手改造以提升性能;同時監(jiān)控消息體大小,對消息體大小平均超過 10 KB 的項目,推動項目啟用壓縮或消息重構(gòu),將消息體控制在 10 KB 以內(nèi)。
?
- 消息鏈路追蹤
一條消息由哪個 ip 、在哪個時間點發(fā)送,又由哪些 ip 、在哪個時間點消費,再加上服務(wù)端統(tǒng)計的消息接收、消息推送的信息,構(gòu)成了一條簡單的消息鏈路追蹤,將消息的生命周期串聯(lián)起來,使用方可通過查詢msgId或事先設(shè)置的 key 查看消息、排查問題。
?
- 過低或有隱患版本檢測
隨著功能的不斷迭代,sdk 版本也會升級并可能引入風(fēng)險。定時上報 sdk 版本,推動使用方升級有問題或過低的版本。
服務(wù)端治理
?
- 集群健康巡檢
如何判斷一個集群是健康的?定時檢測集群中節(jié)點數(shù)量、集群寫入 tps 、消費 tps ,并模擬用戶生產(chǎn)、消費消息。
?
- 集群性能巡檢
性能指標(biāo)最終反映在處理消息生產(chǎn)與消費的時間上。服務(wù)端統(tǒng)計處理每個生產(chǎn)、消費請求的時間,一個統(tǒng)計周期內(nèi),若存在一定比例的消息處理時間過長,則認為這個節(jié)點性能有問題;引起性能問題的原因主要是系統(tǒng)物理瓶頸,比如磁盤 io util 使用率過高,cpu load 高等,這些硬件指標(biāo)通過夜鷹監(jiān)控系統(tǒng)自動報警。
?
- 集群高可用
高可用主要針對 broker 中 master 節(jié)點由于軟硬件故障無法正常工作,slave 節(jié)點自動被切換為 master ,適合消息順序、集群完整性有要求的場景。
?
部分后臺操作展示
主題與消費組申請
生產(chǎn),消費,堆積實時統(tǒng)計
?
?
集群監(jiān)控
踩過的坑
?
社區(qū)對 MQ 系統(tǒng)經(jīng)歷了長時間的改進與沉淀,我們在使用過程中也到過一些問題,要求我們能從深入了解源碼,做到出現(xiàn)問題心不慌,快速止損。
?
- 新老消費端并存時,我們實現(xiàn)的隊列分配算法不兼容,做到兼容即可;
- 主題、消費組數(shù)量多,注冊耗時過長,內(nèi)存 oom ,通過壓縮縮短注冊時間,社區(qū)已修復(fù);
- topic 長度判斷不一致,導(dǎo)致重啟丟消息,社區(qū)已修復(fù);
- centos 6.6 版本中,broker 進程假死,升級 os 版本即可。
MQ 未來展望
?
目前消息保留時間較短,不方便對問題排查以及數(shù)據(jù)預(yù)測,我們接下來將對歷史消息進行歸檔以及基于此的數(shù)據(jù)預(yù)測。
?
- 歷史數(shù)據(jù)歸檔
- 底層存儲剝離,計算與存儲分離
- 基于歷史數(shù)據(jù),完成更多數(shù)據(jù)預(yù)測
- 服務(wù)端升級到 Dledger ,確保消息的嚴格一致
?
了解更多 RocketMQ 信息,可加入社區(qū)交流群,下面是釘釘群,歡迎大家加群留言。
?
總結(jié)
以上是生活随笔為你收集整理的同程旅行基于 RocketMQ 高可用架构实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度解读畅捷通云原生架构转型实战历程
- 下一篇: 【GOTC 预告】王思宇:从 OpenK