騰訊雲分散式高可靠訊息佇列CMQ架構最佳實踐
作者:閆二輝,騰訊雲資深儲存架構師,在大規模儲存、PaaS、虛擬化領域有長期深厚積累。目前主要從事網際網路中介軟體的設計研發相關工作。
騰訊雲基礎產品團隊:繼承騰訊內部沉澱十多年的虛擬化、計算、網路、儲存等基礎能力,深度開發KVM和SDN網路虛擬化等底層技術,開發高可用塊儲存和容器服務,為業界打造一系列高可用高效能的彈性雲產品,以及覆蓋網際網路和傳統行業的綜合解決方案。
內容目錄
1. 訊息佇列的使用場景、價值
2. CMQ底層架構剖析
3. CMQ對比開源rabbitMQ壓測
4. CMQ案例最佳實踐
01|訊息佇列的使用場景
· 訊息的收發解耦:傳送方和接收方不需要了解彼此,甚至不需要知道對方的存在;
· 遮蔽不同平臺之間的差異:不同平臺之間通過訊息來實現互動,僅僅關心訊息的傳送和讀取;
· 削峰填谷,提高系統應對突發能力:傳送訊息端永遠不會堵塞,突發訊息快取在CMQ SERVER 端,消費者按照實際能力讀取訊息;
· 一次生產多次消費:一條訊息可以被多鍾型別消費者訂閱,生產著僅需生產一次即可;
· 跨IDC/WAN傳輸:CMQ支援訊息在不同IDC、城市進行生產、消費,自動就近接入,對業務透明;
02|CMQ底層架構剖析
在分散式大行其道的今天,我們在系統內部、平臺之間廣泛運用訊息中介軟體進行資料交換及解耦。CMQ是騰訊雲內部自研基於的高可靠、強一致、可擴充套件分散式訊息佇列,在騰訊內部包括微信手機QQ業務紅包、騰訊話費充值、廣告訂單等都有廣泛使用。目前已上線騰訊雲對外開放,本文對CMQ 核心技術原理進行分享介紹。
按照使用場景可以將訊息中介軟體粗略分為:高可靠和高效能兩大類。CMQ主要適用於金融、交易、訂單等對可靠性、可用性有較高要求的業務場景。
如圖1以騰訊充值系統為例,該充值系統通過CMQ 對交易模組、發貨部分、結算系統進行非同步解耦、削峰填谷,一方面大大降低了模組間耦合度,另一方面減輕了大量突發請求對後端系統的衝擊。在月初充值該系統一天經過CMQ轉發的訊息超過十億條,每秒峰值超過10w,最高時有數億條訊息通過CMQ的堆積能力緩衝了對後端消費模組的壓力。
圖1-某充值系統結構
CMQ整體結構如圖2所示,本文重點介紹後端broker set實現原理。通常情況下一個set由3個節點組成,通過多副本保證訊息的可靠性、多節點提高系統可用性。當然,可以根據業務的實際需求通過增加set內節點個數來進一步提高可靠性和可用性,CMQ set 內部結構如圖3所示。
圖2-CMQ整體架構圖
圖3-broker set 內部結構圖
下面分別中資料高可靠、強一致,系統可用性,可擴充套件、訊息全路徑追蹤方面分別介紹。
高可靠保證
可靠性保證方面主要包括以下三方面:生產可靠、儲存(堆積)可靠、消費可靠:
· 生產可靠
如上圖3所示,客戶端生產的訊息在set 中超過半數的broker 刷盤成功後會返回確認訊息告知生產訊息成功。如果在一定時間之內客戶端沒有收到確認資訊需要重試來確保訊息傳送成功。
可靠生產帶來的一個問題就是訊息的重複,在網路異常等情況下很可能CMQ broker已經儲存訊息成功只是確認包在網路上丟失了,這樣客戶端重試生產後,在broker上存在兩條重複的訊息。考慮到訊息去重開銷較大,目前訊息的冪等性需要業務邏輯來保證。
· 儲存可靠
CMQSET中一個節點為leader 其他節點為follower,leader 負責所有訊息的生產消費。當生產訊息到達leader 節點後,通過raft 一致性模組將請求順序寫raft log 並同步刷盤,同時將構造好的raft log 按順序通過網路傳送到其他follower節點,follower節點同步刷盤並返回成功。當leader 收到過半數的節點同步成功資訊後將此條請求提交到mq 處理狀態機,由mq 狀態機將請求應用到相應queue。大致邏輯圖4所示。
圖4-資料儲存原理示意圖
由此可見,對於返回客戶端成功的訊息至少是分別在兩個節點磁碟上儲存成功的,這就將磁碟故障引起的資料丟失大大降低。另外資料在磁碟上儲存時會將檢驗結果一同記下來,消費者在消費資料之前CMQ broker 會進行比較,確保訊息是完整有效的。
· 消費可靠
消費者拉取訊息時會指定當前訊息的隱藏時間,在隱藏時間內消費者比較顯式的對訊息進行確認刪除,如果超過隱藏時間沒有主動刪除,此條訊息將重新對外可見,可以繼續消費。
顯式確認刪除訊息是為了防止訊息在投遞、處理過程中異常而導致的訊息丟失。
對於訊息的確認資訊 CMQ broker的處理邏輯和生產訊息過程類似,也是一個寫入的過程,不同的是此時寫入的資料的內容是msgid 和訊息狀態。
強一致實現
假如一個set中有3個節點(A, B, C),A為leader,B C 是follower。如上圖所示,對於返回客戶端成功的請求資料在CMQ 中至少在兩個節點上存在,假設為A B,此時如果leader A故障,B C 兩個follower會自動選舉出一個新leader,CMQ 使用的raft 演算法可以保證這個leader 一定是擁有最全量log 資訊中的一個,在此必定是B。此時B繼續對外服務,B 和A 擁有相同的已經返回確認給使用者的全量資料檢視,資料是強一致的。
對於A 和 B C 所在的網路發生分割槽的情況(如圖5),由於leader A得不到set 中過半節點的回覆所以不能處理請求,B C在選舉超時後會選舉出一個新的leader ,CMQ的接入層會自動進行切換。Raft 演算法保證新leader 同樣具有完成的資料檢視。
可用性保證
如上文所述,master 負責所有訊息的生產消費,當master 故障時SET中其他follower節點會自動選舉出一個新leader,客戶端請求會自動重定向到leader節點,RTO和配置的選舉超時時間有關,目前是在5s左右。大致過程如上圖6所示,具體選舉演算法請參考raft 論文。
CMQ單個set 在CAP理論中優先保證了CP,當SET中過半數節點都正常工作時,才能進行訊息的生產消費。對於SET多個節點同時故障的不可用情況,CMQ強大的監控排程能力能夠快速對queue進行排程遷移恢復服務,將不可用時間降到最低。
橫向擴充套件,無限堆積
上文中SET的概念對使用者來說是透明無感知的,CMQ controller server 根據set的負載情況實時對queue進行排程搬遷。如果某個queue的請求量超過當前set的服務閾值,controller server 可以將queue 路由分佈到多個set 上來提高併發量,對於需要海量堆積的服務來說可以通過路由排程來提升堆積上限,理論上可以達到無限堆積。
目前CMQ只能保證特定情況下訊息的嚴格有序,例如需要保證單個生產程序、單個消費程序,或者queue的消費視窗設定為1等條件。
全路徑訊息trace
CMQ系統中,一條訊息的完整路徑包含生產者、broker、消費者三個角色,每個角色處理訊息的過程中都會在trace 路徑中增加相關的資訊,將這些資訊匯聚即可獲取任意一條訊息的狀態和當前經過的完整路徑,從而為生產環境中的問題排查提供強有力的資料支援。大大降低了業務定位問題的難度。
小結
CMQ是基於raft 演算法來保證資料高可靠、強一致的分散式訊息佇列,主要服務於訂單、交易類業務場景。訊息的冪等性需業務側來保證,在特定情況下可以保證訊息嚴格有序。
對於更側重高效能、高吞吐量業務需求,騰訊雲由另外一個訊息引擎來提供服務,在協議上同時相容kafka,很好的滿足了大資料場景,具體原理請留意後續文章介紹。
03|CMQ對比開源rabbitMQ壓測
RabbitMQ 是具有代表性的開源訊息中介軟體,當前較多地應用於企業系統內,用於對資料一致性、穩定性和可靠性要求較高的場景中。
CMQ也是強調高可靠的訊息傳遞,那騰訊雲的CMQ,對比rabbitMQ有哪些優勢?
• 功能升級
除了生產、消費確認機制,CMQ還提供了消費回溯功能。
使用者指定CMQ儲存生產訊息一定天數,隨後將消費回溯到該時間段內某一時間點,從該點開始重新消費。在使用者業務邏輯異常時,以時間為起點的訊息重放功能對業務恢復非常有幫助。
• 效能優化
網路IO:CMQ能夠批量生產/消費訊息,RabbitMQ則不支援批量生產。在大量小訊息場景中,CMQ具有更少的請求數和更低的平均延遲。
檔案IO:CMQ生產/消費訊息是順序寫單個檔案,並週期落盤儲存,充分利用檔案系統快取。RabbitMQ持久化訊息先入記憶體佇列進行狀態轉換,然後寫日誌快取,最後寫訊息檔案和索引檔案(索引檔案為順序寫、訊息檔案為隨機寫),涉及三次IO操作,效能較差。
CPU:RabbitMQ的日誌快取和狀態轉換運算較複雜,大量耗用CPU。
• 可用性提升
CMQ和RabbitMQ都能夠使用多臺機器進行熱備,提高可用性。CMQ基於Raft演算法實現,簡單易維護。RabbitMQ使用自創的GM演算法(Guaranteed Multicast),學習難度高。
圖1 Raft日誌複製
Raft協議中,Log複製只要大多數節點向Leader返回成功,Leader就可以應用該請求,向客戶端返回成功。
圖2 GM環狀可靠多播
GM可靠多播將叢集中所有節點組成一個環。Log複製依次從Leader向後繼節點傳播,當Leader再次收到該請求時,發出確認訊息在環中傳播,直至Leader再次收到該確認訊息,表明Log在環中所有節點同步完成。
GM演算法要求Log在叢集所有節點同步之後才能向客戶端返回成功;Raft演算法則只要求大多數節點同步完成。Raft演算法在同步路徑上比GM演算法減少了一半的等待時間。
· 壓測結果
經內部嚴格壓測,在同等網路、CPU記憶體環境下,CMQ在保證可靠傳遞的前提下,QPS表現是rabbitMQ的4倍以上。
04|CMQ15年微信春晚紅包案例
春晚紅包活動涉及四個大型系統的聯動,包括微信、微信支付、紅包系統和財付通系統。以下簡單介紹各個系統:
紅包系統:個人紅包的發、搶、拆和列表檢視;
財付通系統:包括支付訂單、非同步入賬流水的高效能儲存,使用者餘額和賬單的實時展示;
微信接入:確保微信使用者公網接入的質量;
微信支付:線上交易的入口。
類似紅包系統的分散式事務是關注的熱點。舉一個典型的例子,“使用者A給使用者B發了10元的紅包”,有以下步驟:
1. 從A帳號中把餘額讀出來
2. 對A帳號做減法操作(減10元)
3. 把結果寫回A帳號中(一次確認)
4. 從B帳號中把餘額讀出來
5. 拆開A傳送給B的紅包,讀出數值
6. 對B帳號做加法操作(加10元)
7. 把結果寫到B帳號中
為了保證資料的一致性,上述步驟只有兩種結果:都成功完成或者都不成功執行回滾。而且這個操作的過程中,對A、B帳號還需引入分散式鎖機制來避免髒資料的問題。在微信紅包這個龐大的分散式叢集內,事情將變的異常複雜。
微信紅包系統引入了騰訊雲 CMQ 以避免分散式事務增加對系統的開銷。同樣A使用者給B使用者發10元紅包的場景,下面介紹引入CMQ 後的新策略。
在上述案例中的第七步,B 使用者拆開了紅包,紅包裡有 10 塊錢。在做最後的入賬操作時由於當天併發壓力大,常出現入賬失敗的情況。
紅包團隊把入帳失敗的請求,全部轉入CMQ。當B使用者更新賬戶餘額失敗時,手機客戶端顯示等待狀態。隨後入賬系統將不斷從 CMQ 重新拉取重試此更新操作。CMQ 保證了這 10 元的入賬訊息永遠不丟,直至它被取出。
在除夕當天,使用者紅包的發、拆、入賬等動作,轉化為了十億級別的海量請求。若使用傳統的事務方式,會放大併發壓力使系統崩潰。
CMQ訊息佇列保證了紅包訊息的可靠儲存、傳遞,實時寫三份保證資料不丟。資金入賬失敗時,入賬系統可非同步的多次重試,從CMQ拉資料,直到成功,起削峰填谷的作用。避免失敗回滾和頻繁輪詢資料庫等傳統方式的弊端。
Q&A
Q1:創業公司怎麼選擇訊息佇列?
A1:開源的訊息佇列有很多,實現機制複雜,運維成本也很高,對於創業企業來說,選擇雲服務商提供的訊息佇列無疑是最省事的,按需使用,成本非常低。
Q2:CMQ生產的訊息,如果未被消費儲存多久?
A2:訊息在佇列中最長的存活時間,從傳送到該佇列開始經過此引數指定的時間後,不論訊息是否被取出過都將被刪除;單位為秒,有效值範圍60-1296000秒,也即1分鐘到15天。
Q3:相比kafka有什麼優勢?
A3:跟kafka相比,各有側重點,kafka更強調吞吐效能。CMQ強調是可靠傳遞,不丟。 CMQ的Kafka版本也即將推出,敬請期待。
Q4:CMQ消費方式是push還是pull?還是兩者結合?
A4:當前queue的模式我們採取的是pull的方式。後續的topic的模式會有push,可以支援push到http端,簡訊,郵件,和queue。
Q5:哪些佇列支援pubsub?
A5: 其實CMQ支援兩種產品形態:queue和topic,對於queue來說沒有pubsub功能,topic有pubsub的能力也能把topic和queue串聯起來使用,queue可以作為topic其中的一個sub方。