DLedger——基於 Raft 的 Commitlog 存儲 Library
故事的起源
自分佈式系統誕生以來,容災和一致性,一直是經常被討論的話題。
Master-Slave 架構是最容易被想到的設計,簡單而易於實現,被早期大部分分佈式系統採用,包括 RocketMQ 早期的高可用架構,其略顯粗陋的一致性保證、缺少自動 Failover 等,並不能滿足需求。
後來,Hadoop 迅猛發展改變了這一面貌。Hadoop 生態裏面的 Zookeeper 組件,可以作爲一個高可用的鎖而存在,由此引發了大量系統通過 Zookeeper 選主,然後主備複製日誌,來達到高可用和一致性的目的。Hadoop 自身 NameNode 組件 的高可用機制便是這一典型實現。
基於 ZooKeeper 的設計,通過一些複雜的寫入 fence,基本可以滿足需求。但 Zookeeper 自身的複雜性,加重了整個設計,在具體實施和運維時,不僅增加資源成本,還累積了系統風險,讓維護人員叫苦不堪。
再後來,Raft 論文的出現,再一次改變了局面。其簡潔易懂的設計,沒有任何外部依賴,就可以輕鬆搞定一個高可靠、高可用、強一致的數據複製系統,讓廣大分佈式系統研發人員如獲至寶。
本文的主角 DLedger 就是這樣的一個實踐者。
DLedger 的定位
DLedger 定位是一個工業級的 Java Library,可以友好地嵌入各類 Java 系統中,滿足其高可用、高可靠、強一致的需求。
和這一定位比較接近的是 Ratis。
Ratis 是一個典型的"日誌 + 狀態機"的實現,雖然其狀態機可以自定義,卻仍然不滿足消息領域的需求。
在消息領域,如果根據日誌再去構建“消息狀態機”,就會產生 Double IO 的問題,造成極大的資源浪費,因此,在消息領域,是不需要狀態機的,日誌和消息應該是合二爲一。
相比於 Ratis,DLedger 只提供日誌的實現,只擁有日誌寫入和讀出的接口,且對順序讀出和隨機讀出做了優化,充分適應消息系統消峯填谷的需求。
DLedger 的純粹日誌寫入和讀出,使其精簡而健壯,總代碼不超過4000行,測試覆蓋率高達70%。而且這種原子化的設計,使其不僅可以充分適應消息系統,也可以基於這些日誌去構建自己的狀態機,從而適應更廣泛的場景。
綜上所述,DLedger 是一個基於 Raft 實現的、高可靠、高可用、強一致的 Commitlog 存儲 Library。
DLedger 的實現
DLedger 的實現大體可以分爲以下兩個部分:
1.選舉 Leader
2.複製日誌
其整體架構如下圖
本文不展開討論實現的細節,詳情可以參考論文1。有興趣的也可以直接參看源碼,項目總體不超過4000行代碼,簡潔易讀。
DLedger 的應用案例
在 Apache RocketMQ 中,DLedger 不僅被直接用來當做消息存儲,也被用來實現一個嵌入式的 KV 系統,以存儲元數據信息。
更多的接入案例,敬請期待。
案例1 DLedger 作爲 RocketMQ 的消息存儲
架構如下圖所示:
其中:
- DLedgerCommitlog 用來代替現有的 Commitlog 存儲實際消息內容,它通過包裝一個 DLedgerServer 來實現複製;
- 依靠 DLedger 的直接存取日誌的特點,消費消息時,直接從 DLedger 讀取日誌內容作爲消息返回給客戶端;
- 依靠 DLedger 的 Raft 選舉功能,通過 RoleChangeHandler 把角色變更透傳給 RocketMQ 的Broker,從而達到主備自動切換的目標
案例2 利用 DLedger 實現一個高可用的嵌入式 KV 存儲
架構圖如下所示:
其中:
- DLedger 用來存儲 KV 的增刪改日誌;
- 通過將日誌一條條 Apply 到本地 Map,比如 HashMap 或者 第三方 的 RocksDB等
整個系統的高可用、高可靠、強一致通過 DLedger 來實現。
社區發展計劃
目前 DLedger 已經成爲 OpenMessaging 中存儲標準的默認實現。DLedger 會維持自身定位不變,作爲一個精簡的 Commitlog 存儲 Library,後續主要是做性能優化和一些必要的特性補充,比如支持手工配置 Leader 節點,支持蛻化成 Master-Slave 架構等。
基於 DLedger 的開發,也可以作爲獨立項目進行孵化,比如 OpenMessaging KV。
歡迎社區的朋友們一起來共建。