中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理

發布時間:2021-10-19 18:34:32 來源:億速云 閱讀:130 作者:柒染 欄目:大數據

本篇文章給大家分享的是有關SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

SOFAStack Scalable Open Financial Architecture Stack 是螞蟻金服自主研發的金融級分布式架構,包含了構建金融級云原生架構所需的各個組件,是在金融場景里錘煉出來的最佳實踐。

SOFAJRaft 是一個基于 Raft 一致性算法的生產級高性能 Java 實現,支持 MULTI-RAFT-GROUP,適用于高負載低延遲的場景。

SOFAJRaft :https://gitee.com/sofastack/sofa-jraft

前言

線性一致讀是在分布式系統中實現 Java volatile 語義,當客戶端向集群發起寫操作的請求并且獲得成功響應之后,該寫操作的結果要對所有后來的讀請求可見。實現線性一致讀常規手段是走 Raft 協議,將讀請求同樣按照 Log 處理,通過日志復制和狀態機執行獲取讀結果返回給客戶端,SOFAJRaft 采用 ReadIndex 替代走 Raft 狀態機的方案。本文將圍繞 Raft Log Read,ReadIndex Read 以及 Lease Read 等方面剖析線性一致讀原理,闡述 SOFAJRaft 如何使用 ReadIndex 和 Lease Read 實現線性一致讀:

  • 什么是線性一致讀?共識算法只能保證多個節點對某個對象的狀態是一致的,以 Raft 為例只能保證不同節點對 Raft Log 達成一致,那么 Log 后面的狀態機的一致性呢?

  • 基于 ReadIndex 和 Lease Read 方式 SOFAJRaft 如何實現高效的線性一致讀?

線性一致讀

什么是線性一致讀? 所謂線性一致讀,一個簡單的例子是在 t1 的時刻我們寫入了一個值,那么在 t1 之后,我們一定能讀到這個值,不可能讀到 t1 之前的舊值(想想 Java 中的 volatile 關鍵字,即線性一致讀就是在分布式系統中實現 Java volatile 語義)。簡而言之是需要在分布式環境中實現 Java volatile 語義效果,即當 Client 向集群發起寫操作的請求并且獲得成功響應之后,該寫操作的結果要對所有后來的讀請求可見。和 volatile 的區別在于 volatile 是實現線程之間的可見,而 SOFAJRaft 需要實現 Server 之間的可見。

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理cdn.nlark.com/yuque/0/2019/png/156670/1561613080963-276c9d3e-a657-40f0-8ffb-d98fd510f6dc.png">

如上圖 Client A、B、C、D 均符合線性一致讀,其中 D 看起來是 Stale Read,其實并不是,D 請求橫跨 3 個階段,而 Read 可能發生在任意時刻,所以讀到 1 或 2 都行。

Raft Log read

實現線性一致讀最常規的辦法是走 Raft 協議,將讀請求同樣按照 Log 處理,通過 Log 復制和狀態機執行來獲取讀結果,然后再把讀取的結果返回給 Client。因為 Raft 本來就是一個為了實現分布式環境下線性一致性的算法,所以通過 Raft 非常方便的實現線性 Read,也就是將任何的讀請求走一次 Raft Log,等此 Log 提交之后在 apply 的時候從狀態機里面讀取值,一定能夠保證這個讀取到的值是滿足線性要求的。

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理

當然,因為每次 Read 都需要走 Raft 流程,Raft Log 存儲、復制帶來刷盤開銷、存儲開銷、網絡開銷,走 Raft Log不僅僅有日志落盤的開銷,還有日志復制的網絡開銷,另外還有一堆的 Raft “讀日志” 造成的磁盤占用開銷,導致 Read 操作性能是非常低效的,所以在讀操作很多的場景下對性能影響很大,在讀比重很大的系統中是無法被接受的,通常都不會使用。

在 Raft 里面,節點有三個狀態:Leader,Candidate 和 Follower,任何 Raft 的寫入操作都必須經過 Leader,只有 Leader 將對應的 Raft Log 復制到 Majority 的節點上面認為此次寫入是成功的。所以如果當前 Leader 能確定一定是 Leader,那么能夠直接在此 Leader 上面讀取數據,因為對于 Leader 來說,如果確認一個 Log 已經提交到大多數節點,在 t1 的時候 apply 寫入到狀態機,那么在 t1 后的 Read 就一定能讀取到這個新寫入的數據。

那么如何確認 Leader 在處理這次 Read 的時候一定是 Leader 呢?在 Raft 論文里面,提到兩種方法:

  • ReadIndex Read

  • Lease Read

ReadIndex Read

第一種是 ReadIndex Read,當 Leader 需要處理 Read 請求時,Leader 與過半機器交換心跳信息確定自己仍然是 Leader 后可提供線性一致讀:

  1. Leader 將自己當前 Log 的 commitIndex 記錄到一個 Local 變量 ReadIndex 里面;

  2. 接著向 Followers 節點發起一輪 Heartbeat,如果半數以上節點返回對應的 Heartbeat Response,那么 Leader就能夠確定現在自己仍然是 Leader;

  3. Leader 等待自己的 StateMachine 狀態機執行,至少應用到 ReadIndex 記錄的 Log,直到 applyIndex 超過 ReadIndex,這樣就能夠安全提供 Linearizable Read,也不必管讀的時刻是否 Leader 已飄走;

  4. Leader 執行 Read 請求,將結果返回給 Client。

使用 ReadIndex Read 提供 Follower Read 的功能,很容易在 Followers 節點上面提供線性一致讀,Follower 收到 Read 請求之后:

  1. Follower 節點向 Leader 請求最新的 ReadIndex;

  2. Leader 仍然走一遍之前的流程,執行上面前 3 步的過程(確定自己真的是 Leader),并且返回 ReadIndex 給 Follower;

  3. Follower 等待當前的狀態機的 applyIndex 超過 ReadIndex;

  4. Follower 執行 Read 請求,將結果返回給 Client。

不同于通過 Raft Log 的 Read,ReadIndex Read 使用 Heartbeat 方式來讓 Leader 確認自己是 Leader,省去 Raft Log 流程。相比較于走 Raft Log 方式,ReadIndex Read 省去磁盤的開銷,能夠大幅度提升吞吐量。雖然仍然會有網絡開銷,但是 Heartbeat 本來就很小,所以性能還是非常好的。

Lease Read

雖然 ReadIndex Read 比原來的 Raft Log Read 快很多,但畢竟還是存在 Heartbeat 網絡開銷,所以考慮做更進一步的優化。Raft 論文里面提及一種通過 Clock + Heartbeat 的 Lease Read 優化方法,也就是 Leader 發送 Heartbeat 的時候首先記錄一個時間點 Start,當系統大部分節點都回復 Heartbeat Response,由于 Raft 的選舉機制,Follower 會在 Election Timeout 的時間之后才重新發生選舉,下一個 Leader 選舉出來的時間保證大于 Start+Election Timeout/Clock Drift Bound,所以可以認為 Leader 的 Lease 有效期可以到 Start+Election Timeout/Clock Drift Bound 時間點。Lease Read 與 ReadIndex 類似但更進一步優化,不僅節省 Log,而且省掉網絡交互,大幅提升讀的吞吐量并且能夠顯著降低延時。

Lease Read 基本思路是 Leader 取一個比 Election Timeout 小的租期(最好小一個數量級),在租約期內不會發生選舉,確保 Leader 不會變化,所以跳過 ReadIndex 的第二步也就降低延時。由此可見 Lease Read 的正確性和時間是掛鉤的,依賴本地時鐘的準確性,因此雖然采用 Lease Read 做法非常高效,但是仍然面臨風險問題,也就是存在預設的前提即各個服務器的 CPU Clock 的時間是準的,即使有誤差,也會在一個非常小的 Bound 范圍里面,時間的實現至關重要,如果時鐘漂移嚴重,各個服務器之間 Clock 走的頻率不一樣,這套 Lease 機制可能出問題。

Lease Read 實現方式包括:

  1. 定時 Heartbeat 獲得多數派響應,確認 Leader 的有效性;

  2. 在租約有效時間內,可以認為當前 Leader 是 Raft Group 內的唯一有效 Leader,可忽略 ReadIndex 中的 Heartbeat 確認步驟(2);

  3. Leader 等待自己的狀態機執行,直到 applyIndex 超過 ReadIndex,這樣就能夠安全的提供 Linearizable Read。

SOFAJRaft 線性一致讀實現

SOFAJRaft 采用 ReadIndex 替代走 Raft 狀態機的方案,簡而言之是依靠 ReadIndex 原則直接從 Leader 讀取結果:所有已經復制到多數派上的 Log(可視為寫操作)被視為安全的 Log,Leader 狀態機只要按照順序執行到此條 Log之后,該 Log 所體現的數據就能對客戶端 Client 可見,具體分解為以下四個步驟:

  • Client 發起 Read 請求;

  • Leader 確認最新復制到多數派的 LogIndex;

  • Leader 確認身份;

  • 在 LogIndex apply 后執行 Read 操作。

通過 ReadIndex 優化,SOFAJRaft 能夠達到 RPC 上限的 80%。上面的步驟中發現第 3 步仍然需要 Leader 通過向 Followers 發送心跳確認自己的 Leader 身份,因為 Raft 集群中的 Leader 身份隨時可能發生改變。所以 SOFAJRaft 采用 Lease Read 的方式把第 3 步 RPC 省略掉。租約理解為 Raft 集群給 Leader 一段租期 Lease 的身份保證,在此期間不會剝奪 Leader 的身份,這樣當 Leader 收到 Read 請求之后,如果發現租期尚未到期,無需再通過和 Followers 通信來確認自己的 Leader 身份,這樣跳過第 3 步的網絡通信開銷。通過 Lease Read 優化,SOFAJRaft 幾乎已經能夠達到 RPC 的上限。然而通過時鐘維護租期本身并不是絕對的安全(時鐘漂移問題),所以 SOFAJRaft 默認配置是線性一致讀,因為通常情況下線性一致讀性能已足夠好。

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理

ReadIndex Read 實現

默認情況下,SOFAJRaft 提供的線性一致讀是基于 Raft 協議的 ReadIndex 實現,三副本的情況下 Leader 讀的吞吐接近于 RPC 的吞吐上限,延遲取決于多數派中最慢的一個 Heartbeat Response。使用 Node#readIndex(byte [] requestContext, ReadIndexClosure done) 發起線性一致讀請求,當安全讀取時傳入的 Closure 將被調用,正常情況下從狀態機中讀取數據返回給客戶端, SOFAJRaft 將保證讀取的線性一致性。線性一致讀在任何集群內的節點發起,并不需要強制要求放到 Leader 節點上,允許在 Follower 節點執行,因此大大降低 Leader 的讀取壓力。

SOFAJRaft 基于 Raft 協議的 ReadIndex 線性一致讀實現是調用 RaftServerService#handleReadIndexRequest 接口根據當前節點狀態為 STATE_LEADER,STATE_FOLLOWER 以及 STATE_TRANSFERRING 情況處理 ReadIndex 請求:

1、當前節點狀態是 STATE_LEADER 即為 Leader 節點,接收 ReadIndex 請求調用 readLeader(request, ReadIndexResponse.newBuilder(), done) 方法提供線性一致讀:

  • 檢查當前 Raft 集群節點數量,如果集群只有一個 Peer 節點直接獲取投票箱 BallotBox 最新提交索引 lastCommittedIndex 即 Leader 節點當前 Log 的 commitIndex 構建 ReadIndexClosure 響應;

  • 日志管理器 LogManager 基于投票箱 BallotBox 的 lastCommittedIndex 獲取任期檢查是否等于當前任期,如果不等于當前任期表示此 Leader 節點未在其任期內提交任何日志,需要拒絕只讀請求;

  • 校驗 Raft 集群節點數量以及 lastCommittedIndex 所屬任期符合預期,那么響應構造器設置其索引為投票箱 BallotBox 的 lastCommittedIndex,并且來自 Follower 的請求需要檢查 Follower 是否在當前配置;

  • 獲取 ReadIndex 請求級別 ReadOnlyOption 配置,ReadOnlyOption 參數默認值為 ReadOnlySafe,ReadOnlySafe 通過與 Quorum 通信來保證只讀請求的可線性化。按照 ReadOnlyOption 配置為ReadOnlySafe 調用 Replicator#sendHeartbeat(rid, closure) 方法向 Followers 節點發送 Heartbeat 心跳請求,發送心跳成功執行 ReadIndexHeartbeatResponseClosure 心跳響應回調;

  • ReadIndex 心跳響應回調檢查是否超過半數節點包括 Leader 節點自身投票贊成,半數以上節點返回客戶端Heartbeat 請求成功響應,即 applyIndex 超過 ReadIndex 說明已經同步到 ReadIndex 對應的 Log 能夠提供 Linearizable Read。

2、當前節點狀態是 STATE_FOLLOWER 即為 Follower 節點,接收 ReadIndex 請求通過 readFollower(request,  done) 方法支持線性一致讀:

  • 檢查當前 Leader 節點是否為空,如果 Leader 節點為空表示當然任期沒有 Leader 節點;

  • Follower 節點調用 RpcService#readIndex(leaderId.getEndpoint(), newRequest, -1, closure) 方法向 Leader 發送 ReadIndex 請求,Leader 節點調用 readIndex(requestContext, done) 方法啟動可線性化只讀查詢請求,只讀服務添加請求發布 ReadIndex 事件到隊列 readIndexQueue 即 Disruptor 的 Ring Buffer;

  • ReadIndex 事件處理器 ReadIndexEventHandler 通過 MPSC Queue 模型攢批消費觸發使用 executeReadIndexEvents(events) 執行 ReadIndex 事件,輪詢 ReadIndex 事件封裝 ReadIndexState 狀態列表構建 ReadIndexResponseClosure 響應回調提交給 Leader 節點處理 ReadIndex 請求;

  • Leader 節點調用 handleReadIndexRequest(request, readIndexResponseClosure) 方法進行 readLeader 線性一致讀過程,返回投票箱 BallotBox 的 lastCommittedIndex。ReadIndex 響應回調遍歷狀態列表記錄當前提交日志 Index,檢查申請狀態機最新 Log Entry 的 committedIndex 是否已經申請即比較狀態機 appliedIndex 是否大于等于當前 committedIndex。由于 Leader 節點處理添加 Log Entry 請求發送心跳后投票箱 BallotBox 更新 lastCommittedIndex,當 Leader 節點的 lastCommittedIndex 大于當前的 lastCommittedIndex 就會創建提交 Log Entry 異步任務發布到 taskQueue 隊列,申請任務處理器 ApplyTaskHandler 執行提交 LogEntry 申請任務,通知 Follower 節點最新申請的 committedIndex 已經更新。如果當前申請狀態機的 applyIndex 超過 ReadIndex,那么通知 ReadIndex 請求成功返回給客戶端。當前 Follower 節點落后于 Leader 時把 Leader 節點返回的committedIndex 放到 pendingNotifyStatus 緩存等待 Leader 節點同步完日志更新 applyIndex。

SOFAJRaft 基于 Batch+Pipeline Ack+ 全異步機制的 ReadIndex 核心邏輯:

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理

Lease Read 實現

SOFAJRaft 針對更高性能要求場景保證集群內機器的 CPU 時鐘同步需求,采用 Clock+Heartbeat 的 Lease Read 優化,通過服務端設置 RaftOptions 的 ReadOnlyOption 參數為 ReadOnlyLeaseBased 實現,ReadOnlyLeaseBased 通過依賴 Leader 租約確保只讀請求的可線性化,可能受時鐘漂移的影響。如果時鐘漂移無限制,Leader 節點可能保持租約長于應有的時間(時鐘可以向后移動/暫停而沒有任何限制),此種情況下 ReadIndex 是不安全的。

SOFAJRaft 基于 Lease Read 線性一致讀實現是通過 Leader 節點調用 handleReadIndexRequest 接口接收 ReadIndex 請求獲取 ReadIndex 請求級別 ReadOnlyOption 配置,當 ReadOnlyOption 配置為 ReadOnlyLeaseBased 時確認 Leader 租約是否有效即檢查 Heartbeat 間隔是否小于 election timeout 時間,Leader 租約超時需要轉變為 ReadIndex 模式。Leader 租約有效期間認為當前 Leader 是 Raft Group 內的唯一有效 Leader,忽略 ReadIndex 發送 Heartbeat 確認身份步驟,直接返回 Follower 節點和本地節點 Read 請求成功響應。Leader 節點繼續等待狀態機執行,直到 applyIndex 超過 ReadIndex 安全提供 Linearizable Read。

SOFAJRaft 基于時鐘和心跳實現的線性一致讀 Lease Read 優化邏輯:

SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理

本文圍繞 Raft Log Read,ReadIndex Read 以及 Lease Read 線性一致讀實現細節方面剖析 SOFAJRaft 線性一致讀基本原理,闡述 SOFAJRaft 如何使用 Batch+Pipeline Ack+全異步機制和 Clock+Heartbeat 手段優化 ReadIndex 和 Lease Read 線性一致讀具體實現。

以上就是SOFAJRaft如何實現剖析以及SOFAJRaft 實現原理,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

博客| 隆子县| 内乡县| 威远县| 海宁市| 布尔津县| 白水县| 探索| 吴忠市| 遂川县| 东至县| 当涂县| 苏尼特右旗| 湘阴县| 昌宁县| 邵阳县| 临江市| 扶风县| 洞头县| 昌吉市| 偃师市| 容城县| 醴陵市| 五台县| 武隆县| 海兴县| 乐陵市| 安多县| 阿荣旗| 桐乡市| 汝州市| 揭东县| 波密县| 武安市| 卢龙县| 和顺县| 汪清县| 卢湾区| 深泽县| 沙田区| 龙陵县|