春秋五霸,是指東周春秋時期相繼稱霸主的五個諸侯,「霸」,意爲霸主,便是諸侯之領袖。
典型的好比齊桓公,晉文公,春秋時期諸侯國的稱霸,與今天要討論的Raft算法很像。git
Raft 適用於一個管理日誌一致性的協議,相比於 Paxos 協議 Raft 更易於理解和去實現它。
爲了提升理解性,Raft 將一致性算法分爲了幾個部分,包括領導選取(leader selection)、日誌複製(log replication)、安全(safety),而且使用了更強的一致性來減小了必須須要考慮的狀態。github
分佈式存儲系統一般經過維護多個副原本提升系統的availability,帶來的代價就是分佈式存儲系統的核心問題之一:維護多個副本的一致性。算法
Raft協議基於複製狀態機(replicated state machine),即一組server從相同的初始狀態起,按相同的順序執行相同的命令,最終會達到一直的狀態,一組server記錄相同的操做日誌,並以相同的順序應用到狀態機。數據庫
Raft有一個明確的場景,就是管理複製日誌的一致性。安全
如圖,每臺機器保存一份日誌,日誌來自於客戶端的請求,包含一系列的命令,狀態機會按順序執行這些命令。
一致性算法管理來自客戶端狀態命令的複製日誌,保證狀態機處理的日誌中的命令的順序都是一致的,所以會獲得相同的執行結果。服務器
先看一段動畫演示,Understandable Distributed Consensus 。網絡
相比Paxos,Raft算法理解起來直觀的很。併發
Raft算法將Server劃分爲3種狀態,或者也能夠稱做角色:分佈式
負責Client交互和log複製,同一時刻系統中最多存在1個。區塊鏈
被動響應請求RPC,從不主動發起請求RPC。
一種臨時的角色,只存在於leader的選舉階段,某個節點想要變成leader,那麼就發起投票請求,同時本身變成candidate。若是選舉成功,則變爲candidate,不然退回爲follower
狀態或者說角色的流轉以下:
在Raft中,問題分解爲:領導選取、日誌複製、安全和成員變化。
複製狀態機經過複製日誌來實現:
Raft中使用心跳機制來出發leader選舉。當服務器啓動的時候,服務器成爲follower。只要follower從leader或者candidate收到有效的RPCs就會保持follower狀態。若是follower在一段時間內(該段時間被稱爲election timeout)沒有收到消息,則它會假設當前沒有可用的leader,而後開啓選舉新leader的流程。
Term的概念類比中國歷史上的朝代更替,Raft 算法將時間劃分紅爲任意不一樣長度的任期(term)。
任期用連續的數字進行表示。每個任期的開始都是一次選舉(election),一個或多個候選人會試圖成爲領導人。若是一個候選人贏得了選舉,它就會在該任期的剩餘時間擔任領導人。在某些狀況下,選票會被瓜分,有可能沒有選出領導人,那麼,將會開始另外一個任期,而且馬上開始下一次選舉。Raft 算法保證在給定的一個任期最多隻有一個領導人。
Raft 算法中服務器節點之間通訊使用遠程過程調用(RPCs),而且基本的一致性算法只須要兩種類型的 RPCs,爲了在服務器之間傳輸快照增長了第三種 RPC。
RPC有三種:
(1)follower增長當前的term,轉變爲candidate。
(2)candidate投票給本身,併發送RequestVote RPC給集羣中的其餘服務器。
(3)收到RequestVote的服務器,在同一term中只會按照先到先得投票給至多一個candidate。且只會投票給log至少和自身同樣新的candidate。
candidate節點保持(2)的狀態,直到下面三種狀況中的一種發生。
Raft中使用隨機選舉超時時間來解決當票數相同沒法肯定leader的問題。
日誌複製(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算法的實現已經很是多了,https://raft.github.io/#implementations
這裏用ETCD來關注Raft的應用,ETCD目標是構建一個高可用的分佈式鍵值(key-value)數據庫,基於 Go 語言實現。
Etcd 主要用途是共享配置和服務發現,實現一致性使用了Raft算法。
更多Etcd的應用能夠查看文檔:https://coreos.com/etcd/docs/latest/
Zookeeper 使用了一種修改後的 Paxos 協議。
在 Zookeeper 中,始終分爲兩種場景:
在這個場景裏,系統中缺少 Leader(primary),經過一個相似 paxos 協議的過程完成 Leader 選舉。
在 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 必定能夠包括最新的已成功提交的數據。
分佈式協議一個著名問題就是 split brain 問題。
簡單說,就是好比當你的 cluster 裏面有兩個結點,它們都知道在這個 cluster 裏須要選舉出一個 master。那麼當它們兩之間的通訊徹底沒有問題的時候,就會達成共識,選出其中一個做爲 master。可是若是它們之間的通訊出了問題,那麼兩個結點都會以爲如今沒有 master,因此每一個都把本身選舉成 master。因而 cluster 裏面就會有兩個 master。
區塊鏈的分叉其實相似分佈式系統的split brain。
通常來講,Zookeeper會默認設置:
Majority 就是一種 Qunroms 的方式來支持Leader選舉,能夠防止 split brain出現。奇數個節點能夠在相同容錯能力的狀況下節省資源。
兩階段提交系統具備徹底的C,很糟糕的A,很糟糕的P。
首先,兩階段提交協議保證了副本間是徹底一致的,這也是協議的設計目的。再者,協議在一個節點出現異常時,就沒法更新數據,其服務可用性較低。最後,一旦協調者與參與者之間網絡分化,沒法提供服務。
Paxos 協議和Raft算法都是強一致性協議。Paxos只有兩種狀況下服務不可用:一是超過半數的 Proposer 異常,二是出現活鎖。前者能夠經過增長 Proposer 的個數來 下降因爲 Proposer 異常影響服務的機率,後者自己發生的機率就極低。最後,只要能與超過半數的 Proposer 通訊就能夠完成協議流程,協議自己具備較好的容忍網絡分區的能力。