文章摘要:BC-MQ 是中國移動蘇州研發中心結合自身在雲端計算產品和技術的較多積累、自主研發的大雲訊息佇列中介軟體產品,本文詳細解讀了 SOFAJRaft 在其訊息雲服務中的最佳應用實踐。
前言:
高可用的定義,指的是“一個系統經過特有的設計與改造,減少因不確定故障停服的時間,從而對業務使用方來說可以保證其服務的高度可用性”。在生產環境中,往往會存在很多不可預知的故障因素,比如虛擬機器當機、磁碟損壞和網路故障等,因此係統自身的高可用是任何工業級產品所需重點考慮的因素。
對於訊息佇列服務來說,考慮到故障切換和業務感知等問題,傳統的高可用方式(冷備或者熱備)一般都不太適用。在經過多種技術方案對比後,我們發現採用基於 Raft 共識演算法的多副本設計方案可以滿足我們產品的要求,因此在鑑權認證元件和API計量服務元件中,我們整合了螞蟻金服開源的 SOFAJRaft 庫,實現這兩個元件應對單點故障的高可用。
GitHub 地址:https://github.com/sofastack/...
一、背景知識:Raft 共識性演算法是什麼?
Raft 是一種分散式系統中易於理解的共識演算法,該協議本質上是 Paxos 演算法的精簡版,而不同的是依靠 Raft 模組化的拆分以及更加簡化的設計,其實現起來更加容易和方便。[1]
模組化的拆分主要體現在 Raft 把一致性協議劃分為如下幾部分:
- Leader 選舉;
- Membership 變更;
- 日誌複製;
- Snapshot。
而更加簡化的設計則體現在:Raft 不允許類似 Paxos 中的亂序提交、簡化系統中的角色狀態(演算法定義 Leader、Follower 和 Candidate 三種角色)、限制僅 Leader 可寫入、採用隨機超時觸發 Leader Election 機制來避免“瓜分選票”等等。[2]
1.1 Raft 演算法的整體結構概覽
從上面的 Raft 演算法整體結構圖中可以看出,整個分散式系統中同一時刻有且僅有一個 Leader 角色的節點(如圖最右邊的伺服器),只有 Leader 節點可以接受 Client 傳送過來的請求。Leader 節點負責主動與所有 Follower 節點進行網路通訊(如圖左邊兩個伺服器),負責將本地的日誌傳送給所有 Follower 節點,並收集分散式系統中多數派的 Follower 節點的響應。此外,Leader 節點,還需向所有 Follower 節點主動傳送心跳維持領導地位(即:保持存在感)。
所以,只要各個節點上的日誌保持內容和順序是一致的,那麼節點上的狀態機就能以相同的順序執行相同的命令,這樣它們執行的結果也都是一樣的。
1.2 Raft 演算法的三種角色及轉換
- Follower:完全被動,不能傳送任何請求,只接受並響應來自 Leader 和 Candidate 的 Message,每個節點啟動後的初始狀態一般都是 Follower;
- Leader:處理所有來自客戶端的請求、複製 Log 到所有 Follower,並且與 Follower 保持心跳請求;
- Candidate:節點競選 Leader 時的狀態。Follower 節點在參與選舉之前,會將自己的狀態轉換為 Candidate。
1.3 任期與邏輯時鐘概念
- 時間被劃分為多個任期 term(如同總統選舉一樣),term id 按時間軸單調遞增;
- 每一個任期開始後要做的第一件事都是選舉 Leader 節點,選舉成功之後,Leader 負責在該任期內管理整個分散式叢集,“日誌複製”、“通過心跳維護自己的角色”;
- 每個任期至多隻有一個 Leader 節點,也可能沒有 Leader (由於“分票”導致)。
1.4 Raft 演算法的實際應用實現
目前,Raft 演算法已經成熟地應用於諸多知名的開源專案中。業界非常著名的 Etcd(Kubernetes 高可用強一致性的服務發現元件)和 TiKV (高效能開源 KV 儲存)均是 Raft 演算法的實現。
二、BC-MQ 基於 Raft 的高可用設計
為滿足企業上雲和構建萬物相連的物聯網業務需求,中國移動蘇州研發中心結合自身在雲端計算產品和技術的較多積累,研發了大雲訊息佇列中介軟體產品 BC-MQ。該產品基於 Apache 開源社群的 RocketMQ 核心,同時結合雲端 PAAS 產品架構和訊息中介軟體的應用業務需求進行深度優化和定製化的研發,提供了一款可以滿足於雲端場景的高效能、高可靠、低延遲和高可用的工業級產品。
本節從解決原有高可用技術方案的問題視角出發,同時結合選型 SOFAJRaft 庫的緣由,將詳細闡述 BC-MQ 產品中的安全認證和 API 計量採集服務的高可用設計方案(注:這裡不會涉及到安全認證和 API 計量採集元件本身的技術方案細節)。
2.1 GlusterFS+Keepalived 高可用方案與問題
1. GlusterFS+Keepalived 高可用設計方案
在BC-MQ原有的方案中,多組安全認證服務各自獨立部署組建叢集,各個安全認證服務相互獨立,沒有主從關聯,服務本身無狀態,可水平任意擴充套件。安全認證服務的高可用依賴於RPC通訊的客戶端保證,其主要通過負載均衡演算法從安全認證服務叢集選擇一個節點傳送RPC請求來實現租戶級鑑權認證後設資料的獲取。在生產環境中,如果出現其中一個安全認證節點當機不可用時,客戶端的RPC通訊層能夠及時感知並從本地的Node列表中剔除不可用節點。
叢集中有狀態的租戶級安全認證後設資料的強一致性由GlusterFS分散式檔案儲存的同步機制來保證。安全認證服務組建高可用叢集的具體設計方案圖如下所示:
而 BC-MQ 中 API 計量採集服務元件的高可用性則是依靠 Keepalived 元件的冷備模式結合 GlusterFS 分散式檔案儲存的同步機制共同保證,從而在一定程度上解決了 API 計量採集服務的單點不可用問題。API 計量採集服務的具體高可用設計方案圖如下所示:
2. GlusterFS+Keepalived 高可用方案遇到的問題
初步看上面的這種高可用技術方案挺完美的。但是經過驗證和仔細推敲後就發現在生產環境中可能會存在如下幾個問題:
- 上面所述的高可用設計方案中引入了 GlusterFS 分散式檔案儲存系統和 Keepalived 元件,這增加了系統整體的運維複雜度,給運維人員帶來很多人工介入排查和部署的工作負擔;另一方面,GlusterFS 和 Keepalived 本身的可靠性、穩定性和效能指標等問題也需要軟體研發人員重點關注,這增加了系統整體設計的複雜度;
- 在實際的生產環境中,Keepalived 元件採用冷備方式作為高可用方案需要考慮主機故障當機後切換到備機的時間成本消耗。在這段時間內,API 計量服務是短暫不可用的。因此,Keepalived 元件的主備切換會造成業務感知影響,導致一些業務的風險發生。
2.2 基於 SOFAJRaft 庫的高可用設計方案
由於“GlusterFS+Keepalived”的高可用方案存在上一節闡述的兩個問題,所以我們考慮是否可以採用其他的高可用方案來解決這兩個問題?目標:即使生產環境出現部分節點故障後,安全認證和 API 計量元件依舊能夠正常提供服務,做到業務無感知。
為了實現當分散式叢集中的部分節點出現故障停服後,叢集仍然能夠自動選主繼續正常對外提供服務,使得故障對外部業務不會產生任何影響,同時高可用方案又不能依賴外部系統,那我們也就想到了 Raft 演算法。Raft 演算法設計,簡潔易懂,沒有任何外部依賴,可以完成一個高可靠、高可用、強一致的資料複製系統,解決我們前面遇到的問題。
業界有一些 Raft 演算法的實現,目前比較流行的主要有百度開源的Braft和螞蟻金服開源的 SOFAJRaft。從官方 Github 上對兩款開源 Raft 實現框架支援的功能和特性來看,基本相近,但 Braft 是 C/C++ 語言實現的,而 SOFAJRaft 是 JAVA 語言實現的,因此我們從技術棧、整合難易和運維成本等角度綜合考慮,最終選擇了 SOFAJRaft。
1. 為何技術選型 SOFAJRaft 庫?
SOFAJRaft 是一個基於 Raft 一致性演算法的生產級高效能 JAVA 實現,支援 MULTI-RAFT-GROUP,適用於高負載低延遲的場景。使用 SOFAJRaft,使用者可以更加專注於自己的業務領域,由 SOFAJRaft 負責處理所有與 Raft 演算法相關的技術難題,並且 SOFAJRaft 比較易於使用,使用者可以通過 Github 上的幾個示例在很短的時間內掌握並使用它。下面先簡單介紹下 SOFAJRaft 的特性和增強功能點:
其中:
- Membership change 成員管理:叢集內成員的加入和退出不會影響叢集對外提供服務。
- Transfer leader:除了叢集根據演算法自動選出 Leader 之外,還支援通過指令強制指定一個節點成為 Leader。
- Fault tolerance 容錯性:當叢集內有節點因為各種原因不能正常執行時,不會影響整個叢集的正常工作。
- 多數派故障恢復:當叢集內半數以上的節點都不能正常服務的時候,正常的做法是等待叢集自動恢復,不過 SOFAJRaft 也提供了 Reset 的指令,可以讓整個叢集立即重建。
- Metrics:SOFAJRaft 內建了基於 Metrics 類庫的效能指標統計,具有豐富的效能統計指標,利用這些指標資料可以幫助使用者更容易找出系統效能瓶頸。
為了提供支援生產環境執行的高效能,SOFAJRaft 主要做了如下幾部分的效能優化,其中:
- 並行 append log:在 SOFAJRaft 中 Leader 本地持久化 Log 和向 Follower 傳送 Log 是並行的。
- 併發複製 Leader 向所有 Follwers 傳送 Log 也是完全相互獨立和併發的。
- 非同步化:SOFAJRaft 中整個鏈路幾乎沒有任何阻塞,完全非同步的,是一個完全的 Callback 程式設計模型。
因此,綜上所述我們最終選用 SOFAJRaft 的理由如下:
- SOFAJRaft 基於 JAVA 實現,能夠很方便的與 BC-MQ 中安全認證服務和 API 計量服務元件進行整合。
- SOFAJRaft 作為一個實現 Raft 協議的框架,提供了易於實現的狀態機介面,只需要實現它提供的介面即可完成高可用的改造。
- 從實際的驗證結果來說,SOFAJRaft 的效能和穩定效能夠完全滿足甚至超過我們的預期。
- SOFAJRaft 的功能效能夠解決上面篇幅中 BC-MQ 原有“GlusterFS+Keepalived”高可用方案中所遇到的問題。
2. BC-MQ 元件整合 SOFAJRaft 的優化設計
BC-MQ在整合SOFAJRaft庫後在部署架構、資料持久化和高可用模式上都進行了能力升級,較好地解決了“GlusterFS+Keepalived”中的問題。
- 部署架構:整合SOFAJRaft庫後,BC-MQ的安全認證和API計量服務的高可用部署不再依賴“GlusterFS+Keepalived”這兩個外部元件;安全認證和API計量服務元件按照配置檔案獨立部署組成相應的RaftGroup即可對外提供服務;
- 資料持久化:資料的強一致性不再依賴“GlusterFS分散式檔案儲存”。通過SOFAJRaft的日誌複製和狀態機,實現叢集中Leader節點和Follower節點的資料同步保證主備節點的資料一致性;
- 高可用模式:從原有的“KeepaLived冷備切換”轉變為“Raft自動Leader選舉”,發生故障後,API計量服務仍然能夠對外正常提供服務,故障轉移的過程無需運維人員介入;
元件服務端的狀態機介面實現
針對具體的業務應用而言(對 BC-MQ 來說,就是 API 計量統計和安全認證鑑權),狀態機(StateMachine)是業務邏輯實現的主要介面,狀態機執行在每個Raft節點上,提交的 任務 Task 如果成功,最終都會複製應用到分散式叢集中的每個節點的狀態機上。
在 SOFAJRaft 中提供了一個已經具備絕大部分預設實現的抽象適配類— StateMachineAdapter,直接繼承它可以使得業務應用避免實現所有的介面。我們根據 BC-MQ 元件改造的需求,對部分介面做了如下的實現:
1. void onApply(Iterator iter):該方法是 SOFAJRaft 中最為核心的介面。在整個分散式叢集環境中,待同步的資料會封裝成 LogEntry 複製到其他節點。在資料同步完成之後,程式會提交到自身狀態機的這個方法中執行。在 BC-MQ 中,API 計量採集服務在計量統計資料日誌同步至 Follower 節點後,SOFAJRaft 在業務狀態機的 onApply 方法中呼叫 API 計量採集服務元件的儲存介面進行持久化。
2. void onLeaderStart(long term)/void onLeaderStop(Status status):這個兩個方法是在節點通過選舉成為 Leader 和失去 Leader 資格時呼叫,BC-MQ 的安全認證和 API 計量服務元件本身也維護了 Raft 的角色狀態(這裡的角色狀態與 SOFAJRaft 本身的是保持一致的)。在節點的角色發生轉變的時候,需要呼叫這個方法,將元件的角色和狀態轉變一致。這樣實現主要是與 BC-MQ 的業務場景相關,在叢集中經過重新選舉後節點角色轉變時,只有API 計量元件服務的 Leader 節點才能夠執行訊息佇列的 API 計量採集相關的定時任務。
3. void onSnapshotSave(SnapshotWriter writer, Closure done)/boolean onSnapshotLoad(SnapshotReader reader):這兩個方法是 SOFAJRaft 快照相關的介面呼叫,快照本身的作用就是在有新的節點加入到 SOFAJRaft Group 時,不需要載入全部的 Log 日誌資料,而只需要從最近的 index 開始載入,這可以節省從 Leader 節點同步大量日誌資訊所造成的網路通訊開銷。BC-MQ 的安全認證和 API 計量採集服務元件實現了這兩個方法,用於實現快照的特性。
客戶端請求重定向機制優化
SOFAJRaft 中預設只有 Leader 節點能夠被客戶端訪問到,所有的日誌提交都需要先提交到叢集的 Leader 節點,然後由Leader節點同步到其他的 Follower 節點。BC-MQ 的安全認證服務和 API 計量服務元件通過 SOFAJRaft 改造後,在 BC-MQ 中原有的客戶端 RPC 請求訪問方式也需要經過一些優化設計,為了讓客戶端能夠實時感知到分散式叢集環境中當前的 Leader 節點,因此需要在客戶端快取一個叢集的節點列表 NodeList 和 LeaderId。
僅僅在客戶端維護一個本地快取還不夠,因為如果叢集中的 Leader 節點出現了當機的故障時,叢集會發生重新選舉,那麼客戶端快取的 Leader 節點資訊就會過期,這就需要客戶端就能夠感知到 Leader 節點的變化。為解決這個問題,我們採用了 RPC 請求重定向機制來保證,一旦RPC請求傳送到了叢集中的 Follower 節點,那麼 Follower 會將該請求重定向到 Leader。以下為 BC-MQ 客戶端通訊重定向機制優化設計圖:
三、BC-MQ 的高可用與節點管理性驗證
下面展示的是 BC-MQ 的安全認證服務和 API 計量服務元件的部分測試用例,從用例的實際執行情況來看,與我們的預期結果完全一致可以滿足生產環境高可用的業務場景。
序號 | 具體業務場景 | 預期結果 | 實際結果 |
---|---|---|---|
1 | 安全認證元件3節點部署,Kill掉其中1個節點,客戶端持續釋出/訂閱帶鑑權的訊息 | 安全認證元件Leader角色轉換,客戶端釋出/訂閱帶鑑權訊息無任何影響 | 與預期一致 |
2 | 安全認證的5節點部署,Kill掉其中2個節點,客戶端持續釋出/訂閱帶鑑權的訊息 | 安全認證元件Leader角色轉換,客戶端釋出/訂閱帶鑑權訊息無任何影響 | 與預期一致 |
3 | API計量元件3節點部署,Kill掉其1個節點,客戶端持續;釋出/訂閱帶鑑權的訊息 | API計量元件Leader角色轉換,輸出的API計量檔案正確 | 與預期一致 |
4 | API計量元件5節點部署,Kill掉其2個節點,客戶端持續釋出/訂閱帶鑑權的訊息 | API計量元件Leader角色轉換,輸出的API計量檔案正確 | 與預期一致 |
5 | 在叢集中模擬出現網路分割槽(對稱/非對稱)的場景,安全認證服務叢集是否會出現腦裂現象,鑑權認證資料是否正確 | 網路分割槽(對稱/非對稱)場景下,叢集不會出現腦裂,並且鑑權資料是正確的 | 與預期一致 |
6 | 在叢集中模擬出現網路分割槽(對稱/非對稱)的場景,API計量服務叢集是否會出現腦裂現象,API計量資料是否正確 | 網路分割槽(對稱/非對稱)場景下,叢集不會出現腦裂,並且API計量資料是正確的 | 與預期一致 |
7 | 在3節點組成的安全認證服務叢集的負載工作的場景下,向該RaftGroup新增1/2節點,客戶端持續釋出/訂閱帶鑑權的訊息 | 客戶端釋出/訂閱帶鑑權訊息無任何影響 | 與預期一致 |
8 | 在5節點組成的安全認證服務叢集的負載工作的場景下,移除該RaftGroup中的1/2節點,客戶端持續釋出/訂閱帶鑑權的訊息 | 客戶端釋出/訂閱帶鑑權訊息無任何影響 | 與預期一致 |
四、總結
本文主要介紹了中國移動蘇州研發中心自主研發的 BC-MQ 產品中兩個重要元件—安全認證和 API 計量服務是如何通過整合開源的 SOFAJRaft 庫解決原來“GlusterFS+Keepalived”高可用方案中遇到的問題,以實現大規模訊息佇列雲服務叢集的高可用部署優化方案。由於文章篇幅的原因,本文沒有對 BC-MQ 本身多項重要的特性進行詳細介紹,作者將在後續的文章中繼續進行闡述。同時,限於筆者的才疏學淺,對本文內容可能還有理解不到位的地方,如有闡述不合理之處還望留言一起探討。
五、參考文獻
[1] Diego Ongaro and John Ousterhout. Raft Paper. 2013
[2] 詳解螞蟻金服 SOFAJRaft | 生產級高效能 Java 實現
作者介紹:
胡宗棠,中國移動蘇州研發中心雲端計算中介軟體團隊負責人,Apache RocketMQ Committer,Linux OpenMessaging Advisory Borad Member,SOFAJRaft Contributor,熟悉分散式訊息中介軟體的設計原理、架構以及各種應用場景。