一致性算法 - Raft協議實踐(SOFAJRaft剖析)

-     SOFAJRaft 概述    -算法

我們對Raft協議已經進行了原理的解析,接下去我們從經過SOFAJRaft 框架的核心流程剖析加深對Raft協議的理解。SOFAJRaft 是一個純 Java 的 Raft 算法實現庫, 基於百度 braft 實現而來, 使用 Java 重寫了全部功能, 支持:緩存

  • 領導人選舉和基於優先級的半肯定性領導人選舉。微信

  • 日誌複製和恢復。網絡

  • 快照和日誌壓縮。架構

  • 只讀成員(learner)。併發

  • 集羣成員管理,添加節點,刪除節點,替換節點等。app

  • 徹底併發複製。負載均衡

  • 容錯能力。框架

  • 非對稱網絡分區容忍性。分佈式

  • 當法定人數同伴都死亡的解決方法。

  • 管道複製

  • 線性一致讀,ReadIndex/LeaseRead。

額外擴展了一些功能:

  • 對稱網絡分區容忍性

  • 重啓後的轉移領袖、負載均衡場景實現

  • 更豐富的指標統計展現

  • 經過Jepsen一致性驗證測試

  • 包含嵌入式分佈式KV存儲實現

總體項目以下:

-     領袖選舉    -

SOFAJRaft 的選舉主要經過判單兩個屬性:LogIndex 和 Term;Term 即任期,LogIndex即提交到 raft group 中的任務都將序列化爲一條日誌存儲下來,每條日誌一個編號,在整個 raft group 內單調遞增並複製到每一個 raft 節點。能夠理解爲事務id。投票處理的邏輯主要在 com.alipay.sofa.jraft.core.NodeImpl中,主要有四個函數:
  • 處理處理預投票請求

    Message handlePreVoteRequest(request)

  • 預投票

    void preVote()

  • 處理投票請求

    Message handleRequestVoteRequest(request)

  • 投票

    electSelf()

總體流程以下:

  • Candidate(候選人) 被 Election timeout觸發

  • Candidate 開始嘗試發起 pre-vote 預投票

  • Follower(追隨者) 判斷是否定可該 pre-vote request

  • Candidate 根據 pre-vote response 來決定是否發起 RequestVoteRequest

  • Follower 判斷是否定可該 RequestVoteRequest

  • Candidate 根據 response 來判斷本身是否當選

使用預投票能夠防止網絡抖動等特殊緣由引發的瞬時失聯節點無端搗亂:候選者在發起投票以前,先發起預投票,若是沒有獲得半數以上節點的反饋,則候選者就會識趣的放棄參選,也就不會擡升全局的 Term。

投票源碼:

預投票源碼:

-     存儲機制    -

SOFAJRaft 存儲模塊分爲:

  • Log 存儲記錄 Raft 配置變動和用戶提交任務日誌,把日誌從 Leader 複製到其餘節點上面;

    • LogStorage 是日誌存儲實現,默認實現基於 RocksDB 存儲,經過 LogStorage 接口擴展自定義日誌存儲實現;核心接口包括:

      • 返回日誌裏的首/末個日誌索引;

      • 按照日誌索引獲取 Log Entry 及其任期;

      • 把單個/批量 Log Entry 添加到日誌存儲;

      • 從 Log 存儲頭部/末尾刪除日誌;

      • 刪除全部現有日誌,重置下任日誌索引。

    • LogManager 負責調用底層日誌存儲 LogStorage,針對日誌存儲調用進行緩存、批量提交、必要的檢查和優化。

      • checkAndResolveConflict(entries, done)

        1. 檢查Node節點,解決日誌衝突。

        2. 配置管理器:緩存配置變動

        3. LogsInMemory緩存日誌Entries

      • offerEvent(done, type)

        Disruptor隊列發佈other類型事件

      • appendToStorage(toAppend)

        回調事件處理器StableClosureEventHandler存儲日誌

  • Meta 存儲即元信息存儲記錄 Raft 實現的內部狀態,好比當前 term,、投票給哪一個節點等信息

    • RaftMetaStorage 元信息存儲實現,定義 Raft 元數據的 Metadata 存儲模塊核心 API 接口包括:

      • 設置/獲取 Raft 元數據的當前任期 Term;

      • 分配/查詢 Raft 元信息的 PeerId 節點投票。

  • Snapshot 存儲用於存放用戶的狀態機 Snapshot 及元信息,用於Node重啓重建整個狀態機實例。

    • SnapshotStorage 用於 snapshot 存儲實現,定義 Raft 狀態機的 Snapshot 存儲模塊核心接口包括:

      • 設置 filterBeforeCopyRemote ,爲 true 表示複製到遠程以前過濾數據;

      • 建立快照編寫器;

      • 打開快照閱讀器;

      • 從遠程 Uri 複製數據;

      • 啓動從遠程 Uri 複製數據的複製任務;

      • 配置 SnapshotThrottle,SnapshotThrottle 用於重盤讀/寫場景限流的,好比磁盤讀寫、網絡帶寬。

    • SnapshotExecutor 用於 snapshot 實際存儲、遠程安裝、複製的管理。

      • 狀態機快照 doSnapshot(done)

      • 安裝快照 installSnapshot(request, response, done)。

LogManager 調用日誌存儲 LogStorage 實現邏輯:

SnapshotExecutor 狀態機快照和遠程安裝鏡像實現邏輯:

-     一致性狀態機    -

經過存儲的設計,在引入狀態機機制,就能夠完成一致性狀態機。SOFAJRaft狀態機組成有:

  • StateMachine:業務邏輯實現的主要接口,狀態機運行在每一個 raft 節點上,提交的 task 若是成功,最終都會複製應用到每一個節點的狀態機上。,核心是 onApply(Iterator) 方法,應用經過 Node#apply(task) 提交的日誌到業務狀態機。

  • FSMCaller:封裝對業務 StateMachine 的狀態轉換的調用以及日誌的寫入等,一個有限狀態機的實現,作必要的檢查、請求合併提交和併發處理等。

SOFAJRaft Node節點利用日誌複製完成數據同步,主要組成有:

  • Replicator:用於 leader 向 follower 複製日誌,也就是 raft 中的 appendEntries 調用,包括心跳存活檢查等。

  • ReplicatorGroup: 用於單個 RAFT Group 管理全部的 replicator,必要的權限檢查和派發。

-     總結    -

      本文經過簡單介紹了下SOFAJRaft的選舉實現、存儲機制、狀態機和日誌複製四個方面。基本上完成了Raft實現的核心實現。但SOFAJRaft還有更多核心及優化,由於篇幅緣由沒有進入細細剖析。若是我們自實現Raft協議,基本上也是實現這幾個主流程便可完成簡版Raft了。關於Raft協議暫時先告一段路,接下去準備開寫ZAB協議。

-     做者介紹    -

林淮川

畢業於西安交通大學;奈學教育《百萬架構師訓練營》講師、企業級源碼內源負責人,前大樹金融高級架構師、技術委員會開創者、技術總監;前天陽宏業交易事業部技術主管;多年互聯網金融行業(ToB)經驗。


 

本文分享自微信公衆號 - 川聊架構(gh_44ec4115d261)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索