從分散式一致性到共識機制(二)Raft演算法

邴越發表於2018-04-16
春秋五霸說開

春秋五霸,是指東周春秋時期相繼稱霸主的五個諸侯,“霸”,意為霸主,即是諸侯之領袖。
典型的比如齊桓公,晉文公,春秋時期諸侯國的稱霸,與今天要討論的Raft演算法很像。

春秋五霸

一、更加直觀的Raft演算法

Raft 適用於一個管理日誌一致性的協議,相比於 Paxos 協議 Raft 更易於理解和去實現它。
為了提高理解性,Raft 將一致性演算法分為了幾個部分,包括領導選取(leader selection)、日誌複製(log replication)、安全(safety),並且使用了更強的一致性來減少了必須需要考慮的狀態。

1.解決什麼問題

分散式儲存系統通常通過維護多個副本來提高系統的availability,帶來的代價就是分散式儲存系統的核心問題之一:維護多個副本的一致性。

Raft協議基於複製狀態機(replicated state machine),即一組server從相同的初始狀態起,按相同的順序執行相同的命令,最終會達到一直的狀態,一組server記錄相同的操作日誌,並以相同的順序應用到狀態機。

replicated state machine

Raft有一個明確的場景,就是管理複製日誌的一致性。

如圖,每臺機器儲存一份日誌,日誌來自於客戶端的請求,包含一系列的命令,狀態機會按順序執行這些命令。
一致性演算法管理來自客戶端狀態命令的複製日誌,保證狀態機處理的日誌中的命令的順序都是一致的,因此會得到相同的執行結果。

state machine

2.Raft概覽

先看一段動畫演示,Understandable Distributed Consensus

相比Paxos,Raft演算法理解起來直觀的很。

Raft演算法將Server劃分為3種狀態,或者也可以稱作角色:

  • Leader

負責Client互動和log複製,同一時刻系統中最多存在1個。

  • Follower

被動響應請求RPC,從不主動發起請求RPC。

  • Candidate

一種臨時的角色,只存在於leader的選舉階段,某個節點想要變成leader,那麼就發起投票請求,同時自己變成candidate。如果選舉成功,則變為candidate,否則退回為follower

狀態或者說角色的流轉如下:

state

在Raft中,問題分解為:領導選取、日誌複製、安全和成員變化。

複製狀態機通過複製日誌來實現:

  • 日誌:每臺機器儲存一份日誌,日誌來自於客戶端的請求,包含一系列的命令
  • 狀態機:狀態機會按順序執行這些命令
  • 一致性模型:分散式環境下,保證多機的日誌是一致的,這樣回放到狀態機中的狀態是一致的

二、Raft演算法流程

Raft中使用心跳機制來出發leader選舉。當伺服器啟動的時候,伺服器成為follower。只要follower從leader或者candidate收到有效的RPCs就會保持follower狀態。如果follower在一段時間內(該段時間被稱為election timeout)沒有收到訊息,則它會假設當前沒有可用的leader,然後開啟選舉新leader的流程。

1.Term

Term的概念類比中國歷史上的朝代更替,Raft 演算法將時間劃分成為任意不同長度的任期(term)。

任期用連續的數字進行表示。每一個任期的開始都是一次選舉(election),一個或多個候選人會試圖成為領導人。如果一個候選人贏得了選舉,它就會在該任期的剩餘時間擔任領導人。在某些情況下,選票會被瓜分,有可能沒有選出領導人,那麼,將會開始另一個任期,並且立刻開始下一次選舉。Raft 演算法保證在給定的一個任期最多隻有一個領導人。

中國歷史

2.RPC

Raft 演算法中伺服器節點之間通訊使用遠端過程呼叫(RPCs),並且基本的一致性演算法只需要兩種型別的 RPCs,為了在伺服器之間傳輸快照增加了第三種 RPC。

RPC有三種:

  • RequestVote RPC:候選人在選舉期間發起
  • AppendEntries RPC:領導人發起的一種心跳機制,複製日誌也在該命令中完成
  • InstallSnapshot RPC: 領導者使用該RPC來傳送快照給太落後的追隨者
3.選舉流程

(1)follower增加當前的term,轉變為candidate。
(2)candidate投票給自己,併傳送RequestVote RPC給叢集中的其他伺服器。
(3)收到RequestVote的伺服器,在同一term中只會按照先到先得投票給至多一個candidate。且只會投票給log至少和自身一樣新的candidate。

candidate節點保持(2)的狀態,直到下面三種情況中的一種發生。

  • 該節點贏得選舉。即收到大多數的節點的投票。則其轉變為leader狀態。
  • 另一個伺服器成為了leader。即收到了leader的合法心跳包(term值等於或大於當前自身term值)。則其轉變為follower狀態。
  • 一段時間後依然沒有勝者。該種情況下會開啟新一輪的選舉。

Raft中使用隨機選舉超時時間來解決當票數相同無法確定leader的問題。

4.日誌複製

日誌複製(Log Replication)主要作用是用於保證節點的一致性,這階段所做的操作也是為了保證一致性與高可用性。

當Leader選舉出來後便開始負責客戶端的請求,所有事務(更新操作)請求都必須先經過Leader處理,日誌複製(Log Replication)就是為了保證執行相同的操作序列所做的工作。

在Raft中當接收到客戶端的日誌(事務請求)後先把該日誌追加到本地的Log中,然後通過heartbeat把該Entry同步給其他Follower,Follower接收到日誌後記錄日誌然後向Leader傳送ACK,當Leader收到大多數(n/2+1)Follower的ACK資訊後將該日誌設定為已提交併追加到本地磁碟中,通知客戶端並在下個heartbeat中Leader將通知所有的Follower將該日誌儲存在自己的本地磁碟中。

三、Raft和Paxos的工程應用

Raft演算法的論文相比Paxos直觀很多,更容易在工程上實現。

可以看到Raft演算法的實現已經非常多了,https://raft.github.io/#implementations

1.Raft的應用

這裡用ETCD來關注Raft的應用,ETCD目標是構建一個高可用的分散式鍵值(key-value)資料庫,基於 Go 語言實現。
Etcd 主要用途是共享配置和服務發現,實現一致性使用了Raft演算法。
更多Etcd的應用可以檢視文件:https://coreos.com/etcd/docs/latest/

2.Zookeeper 中的 Paxos

Zookeeper 使用了一種修改後的 Paxos 協議。

在 Zookeeper 中,始終分為兩種場景:

  • Leader activation

在這個場景裡,系統中缺乏 Leader(primary),通過一個類似 paxos 協議的過程完成 Leader 選舉。

  • Active messaging
    在 這個場景裡,Leader 接收客戶端傳送的更新操作,以一種類似兩階段提交的過程在各個 follower (secondary)節點上進行更新操作。

在 Leader activation 場景中完成 leader 選舉及資料同步後,系統轉入 Active messaging 場景,在 active messaging 中 leader 異常後,系統轉入 Leader activation 場景。

無論在那種場景,Zookeeper 依賴於一個全域性版本號:zxid。zxid 由(epoch, count)兩部分組成, 高位的 epoch 部分是選舉編號,每次提議進行新的 leader 選舉時 epoch 都會增加,低位的 count 部分 是 leader 為每個更新操作決定的序號。可以認為,一個 leader 對應一個唯一的 epoch,每個 leader 任期內產生的更新操作對應一個唯一的有序的 count,從而從全域性的視野,一個 zxid 代表了一個更新操作的全域性序號(版本號)。

Zookeeper 通過 zxid 將兩個場景階段較好的結合起來,且能保證全域性的強一致性。由於同一時刻只有一個 zookeeper 節點能獲得超過半數的 follower,所以同一時刻最多隻存在唯一的 leader;每個 leader 利用 FIFO 以 zxid 順序更新各個 follower,只有成功完成前一個更新操作的才會進行下一個更新操作,在同一個 leader 任期內,資料在全域性滿足 quorum 約束的強一致,即讀超過半數的節點 一定可以讀到最新已提交的資料;每個成功的更新操作都至少被超過半數的節點確認,使得新選舉 的 leader 一定可以包括最新的已成功提交的資料。

3.如何解決split brain問題

分散式協議一個著名問題就是 split brain 問題。

簡單說,就是比如當你的 cluster 裡面有兩個結點,它們都知道在這個 cluster 裡需要選舉出一個 master。那麼當它們兩之間的通訊完全沒有問題的時候,就會達成共識,選出其中一個作為 master。但是如果它們之間的通訊出了問題,那麼兩個結點都會覺得現在沒有 master,所以每個都把自己選舉成 master。於是 cluster 裡面就會有兩個 master。

區塊鏈的分叉其實類似分散式系統的split brain。

一般來說,Zookeeper會預設設定:

  • zookeeper cluster的節點數目必須是奇數。
  • zookeeper 叢集中必須超過半數節點(Majority)可用,整個叢集才能對外可用。

Majority 就是一種 Qunroms 的方式來支援Leader選舉,可以防止 split brain出現。奇數個節點可以在相同容錯能力的情況下節省資源。

四、從CAP的角度理解幾種不同的演算法

1.兩階段提交協議

兩階段提交系統具有完全的C,很糟糕的A,很糟糕的P。
首先,兩階段提交協議保證了副本間是完全一致的,這也是協議的設計目的。再者,協議在一個節點出現異常時,就無法更新資料,其服務可用性較低。最後,一旦協調者與參與者之間網路分化,無法提供服務。

2.Paxos和Raft演算法

Paxos 協議和Raft演算法都是強一致性協議。Paxos只有兩種情況下服務不可用:一是超過半數的 Proposer 異常,二是出現活鎖。前者可以通過增加 Proposer 的個數來 降低由於 Proposer 異常影響服務的概率,後者本身發生的概率就極低。最後,只要能與超過半數的 Proposer 通訊就可以完成協議流程,協議本身具有較好的容忍網路分割槽的能力。

參考
Raft一致性演算法
Raft 一致性演算法論文譯文


相關文章