拜占庭將軍問題是最複雜的分散式故障場景,節點除了故障行為,還有惡意行為。必須使用拜占庭容錯演算法(BFT),比如在數字貨幣的區塊鏈技術中。
拜占庭容錯演算法
1.口信訊息型拜占庭問題之解
如果叛將人數為 m,將軍人數不能少於 3m + 1 ,那麼拜占庭 將軍問題就能解決了。
這個演算法有個前提,也就是叛將人數 m,或者說能容忍的叛將數 m,是已知的。在這個演算法中,叛將數 m 決定遞迴迴圈的次數(也就是說,叛將數 m 決定將軍們要進行多少輪作戰資訊協商),即 m+1 輪(所以,你看,只有楚是叛變的,那麼就進行了兩輪)。
2.簽名訊息型拜占庭問題之解
簽名無法偽造,而且對他簽名訊息的內容進行任何更改都會被發現,這樣的話也就會實現無論有多少忠誠的將軍和多少叛 將,忠誠的將軍們總能達成一致的作戰計劃,不過也需要進行m次協商。
3.PBFT 算 法
4.PoW 演算法
非拜占庭容錯演算法
即故障容錯演算法(Crash Fault Tolerance,CFT),CFT 解決的是分散式的系統中存在故障,但不存在惡意節點的場景下 的共識問題。 也就是說,這個場景可能會丟失訊息,或者有訊息重複,但不存在錯誤消 息,或者偽造訊息的情況。常見的演算法有 Paxos 演算法、Raft 演算法、ZAB 協議。
Paxos 演算法,最常用的一批共識演算法都是基於它改進的。比如,Fast Paxos 演算法、 Cheap Paxos 演算法、Raft 演算法、ZAB 協議等等。
Basic Paxos 演算法
描述的是多節點之間如何就某個值(提案 Value)達成共識;
假設我們要實現一個分散式叢集,這個叢集是由節點 A、B、C 組成,客戶端 1 試圖建立值為 3 的 X,客戶端 2 試圖建立值為 7 的 X,這樣要如何達成 共識,實現各節點上 X 值的一致呢?
Raft演算法
從本質上說,Raft 演算法是透過一切以領導者為準的方式,實現一系列值的共識和各節點日誌的一致。這句話比較抽象,我來做個比喻,領導者就是 Raft 演算法中的霸道總裁,透過霸道的“一切以我為準”的方式,決定了 日誌中命令的值,也實現了各節點日誌的一致。Raft 演算法是強領導者模型,叢集中只能有一個“霸道總裁”。
服務節點由三種身份狀態
跟隨者:就相當於普通群眾,默默地接收和處理來自領導者的訊息,當等待領導者心跳 資訊超時的時候,就主動站出來,推薦自己當候選人。
候選人:候選人將向其他節點傳送請求投票(RequestVote)RPC 訊息,通知其他節點 來投票,如果贏得了大多數選票,就晉升當領導者。
領導者:蠻不講理的霸道總裁,一切以我為準,平常的主要工作內容就是 3 部分,處理 寫請求、管理日誌複製和不斷地傳送心跳資訊,通知其他節點“我是領導者,我還活 著,你們現在不要發起新的選舉,找個新領導者來替代我。
領導者選舉過程
每個節點都有一個隨機的超時時間,超過這個時間沒有收到領導者心跳訊號,節點馬上變為候選人,並向其他節點傳送選舉請求,讓他們投票自己做領導者。獲得投票過半的節點就是領導者,然後不斷向其他節點傳送心跳。
節點如何通訊?
在 Raft 演算法中,伺服器節點間的溝通聯絡採用的是遠端過程呼叫(RPC),在領導者選舉 中,需要用到這樣兩類的 RPC:
請求投票(RequestVote)RPC,是由候選人在選舉期間發起,通知各節點進行投票;
日誌複製(AppendEntries)RPC,是由領導者發起,用來複制日誌和提供心跳訊息。
我想強調的是,日誌複製 RPC 只能由領導者發起,這是實現強領導者模型的關鍵之一,希 望你能注意這一點,後續能更好地理解日誌複製,理解日誌的一致是怎麼實現的。
什麼是任期?
1.跟隨者在等待領導者心跳資訊超時後,推舉自己為候選人時,會增加自己的任期號,比 如節點 A 的當前任期編號為 0,那麼在推舉自己為候選人時,會將自己的任期編號增加 為 1。
2.如果一個伺服器節點,發現自己的任期編號比其他節點小,那麼它會更新自己的編號到 較大的編號值。比如節點 B 的任期編號是 0,當收到來自節點 A 的請求投票 RPC 訊息 時,因為訊息中包含了節點 A 的任期編號,且編號為 1,那麼節點 B 將把自己的任期編 號更新為 1。
3.在 Raft 演算法中約定,如果一個候選人或者領導者,發現自己的任期編號比其他節點小, 那麼它會立即恢復成跟隨者狀態。比如分割槽錯誤恢復後,任期編號為 3 的領導者節點 B,收到來自新領導者的,包含任期編號為 4 的心跳訊息,那麼節點 B 將立即恢復成跟 隨者狀態。
4.還約定如果一個節點接收到一個包含較小的任期編號值的投票請求,那麼它會直接拒絕這個請求。比如節點 C 的任期編號為 4,收到包含任期編號為 3 的請求投票 RPC 訊息,那它將拒絕這個訊息。
5.任期編號相同時,日誌完整性高的跟隨者(也就是最後一條日誌項對應的任期編號值 更大,索引號更大),拒絕投票給日誌完整性低的候選人。
隨機超時時間
為了防止同一時間,多個節點同時發起選舉,跟隨者等待領導者心跳資訊超時的時間間隔,是隨機的。
當沒有候選人贏得過半票數,選舉無效了,這時需要等待一個隨機時間間隔,也就是 說,等待選舉超時的時間間隔,是隨機的。
Raft的日誌
道 Raft 除了能實現一系列值的共識之外,還能實現各節點日誌的一致
日誌是由日誌項組成,日誌項是一種資料格式,它主要包含使用者指定的資料,也就是指令,比如set x=3, 還包含一些附加資訊,比如索引值(Log index)、任期編號(Term)。
{1,set x=1,1} | {2,set y=1,1} | {3,set x=2,1} | {4,set x=5,1} |
---|---|---|---|
{5,set x=5,1} | {6,set x=5,1} | {7,set x=5,1} | {8,set x=5,1} |
指令:一條由客戶端請求指定的、狀態機需要執行的指令。你可以將指令理解成客戶端 指定的資料。
索引值:日誌項對應的整數索引值。它其實就是用來標識日誌項的,是一個連續的、單 調遞增的整數號碼。
任期編號:建立這條日誌項的領導者的任期編號。
如何複製日誌?
最佳化後的二階段提交
領導者接收到客戶端命令,首先,領導者進入第一階段,透過日誌複製(AppendEntries)RPC 訊息,將日誌項複製到叢集其他節點上。 接著,如果領導者接收到大多數的“複製成功”響應後,它將日誌項提交到它的狀態機,並返回成功給客戶端。如果領導者沒有接收到大多數的“複製成功”響應,那麼就返回錯誤給客戶端。
學到這裡,有同學可能有這樣的疑問了,領導者將日誌項提交到它的狀態機,怎麼沒通知跟隨者提交日誌項呢? 這是 Raft 中的一個最佳化,領導者不直接傳送訊息通知其他節點提交指定日誌項。因為領導者的日誌複製 RPC 訊息或心跳訊息,包含了當前最大的,將會被提交的日誌項索引值。所以透過日誌複製 RPC 訊息或心跳訊息,跟隨者就可以知道領導者的日誌提交位置資訊。 因此,當其他節點接受領導者的心跳訊息,或者新的日誌複製 RPC 訊息後,就會將這條日 志項提交到它的狀態機。而這個最佳化,降低了處理客戶端請求的延遲,將二階段提交最佳化為 了一段提交,降低了一半的訊息延遲
1.接收到客戶端請求後,領導者基於客戶端請求中的指令,建立一個新日誌項,並附加到本地日誌中
2.領導者透過日誌複製 RPC,將新的日誌項複製到其他的伺服器。
3.當領導者將日誌項,成功複製到大多數的伺服器上的時候,領導者會將這條日誌項提交到它的狀態機中。
4.領導者將執行的結果返回給客戶端。
5.當跟隨者接收到心跳資訊,或者新的日誌複製 RPC 訊息後,如果跟隨者發現領導者已經提交了某條日誌項,而它還沒提交,那麼跟隨者就將這條日誌項提交到本地的狀態機中。
如何實現日誌的一致?
如果某些節點出現當機或者網路故障,日誌就會出現不一致。
在 Raft 演算法中,領導者透過強制跟隨者直接複製自己的日誌項,處理不一致日誌。也就是 說,Raft 是透過以領導者的日誌為準,來實現各節點日誌的一致的。需要你注意的是,跟隨者中的不一致日誌項會被領導者的日誌覆蓋,而且領導者 從來不會覆蓋或者刪除自己的日誌。
Raft成員變更
當往叢集增加節點或者減少節點,節點數不同,選舉結果可能會不同。
本作品採用《CC 協議》,轉載必須註明作者和本文連結