前言
先后拜讀了Paxos made simple和Raft兩篇大作,作為一個學數學出身的人,深感Paxos作者Leslie Lamport邏輯之嚴密,通過層層遞進不斷加強保證的方式推導出Paxos的神級算法,論文雖不長但要徹徹底底明白作者的心思邏輯,還得細細品味一番。筆者也深以為論文正應如此,邏輯嚴密不差絲毫。然而,在拜讀Raft大作之后,筆者卻有一種讀小說后如沐春風之感。Raft作者沒有任何頭腦風暴,清清楚楚用及其通俗的語言闡述了一個工業級的通俗易懂的一致性協議。
Paxos在1990年就已經被提出來了,Raft協議直到2013年才以論文的形式面世,但短短幾年Raft的各種開源和工業級應用卻在以指數級增長,像開源實現etcd就是基于Raft協議實現的。下面我們來詳細了解下Raft協議的真面目。
Raft一致性協議
為了形成多數派,Raft集群一般都是由奇數個server構成。Raft集群中的server總共有三種狀態:
- Leader
- follower
- candidate
一般情況下leader只有一個,其它全部都是follower。leader負責接收并處理所有client的請求,follower是被動的,它只能接收leader的請求但從不主動發出任何請求。第三個狀態candidate負責選舉leader,一旦server身份確定則自動轉換到leader或follower狀態。下圖展示了不同狀態之間轉換的條件:
Raft協議中另外一個非常重要的術語就是term(任期),Raft把時間軸按從左到右劃分成任意長度的任期。每個term都是從一次選舉開始,當某個candidate被選舉為leader后,它便成為整個term的leader,選舉結束后開始處理client的請求。
Leader選舉
與Paxos選舉算法相比,Raft協議選舉算法顯得相當的簡單。從以上的狀態轉換圖可以看出來,只有當server處于candidate狀態時才能夠進行選舉流程。開始選舉之前server首先增加它當前的term,然后才會轉換到candidate狀態。
- A啟動后處于follower狀態,當follower在timer時間內沒有收到leader的心跳請求,會轉換到candidate狀態并觸發選舉流程。Raft協議使用多數派來保證leader的唯一性,當收到全部或者多數節點的同意后,candidate認為自己可以成為leader。一旦leader確定身份后,它會發送心跳信息給其它的server,以此來宣告它的統治時代到來。
- 如下圖所示,當A發出投票后進入等待狀態,此時由于集群中已經存在leader,A收到B的請求告知B已經是leader。這時如果B的term大于或者等于A當前的term,則A自動轉換成follower,如果A當前的term大于B的term,則A維持在candidate狀態繼續等待其它請求。在Raft協議中所有的請求有一個共同的原則,就是如果請求中的term大于本地當前的term,server會自動轉換成follower狀態。
- 還有一種情況就是所有的server都處于candidate狀態,發出自己的投票,大家的投票比較分散,所以沒有server拿到大多數的投票,也就不能產生所謂的leader,這就是通常所說的split vote。那這種情況下怎么辦呢?Raft為了避免這種情況發生的概率,采取了隨機超時的機制,也就是說當某個server發起一次投票之前,他會隨機設置超時時間,以此來減小不同server同時發出投票造成split vote的概率。
日志復制
Raft集群中每個server都維護自己的有限狀態機,日志復制就是用來保證集群中有限狀態機的一致性。一般過程如圖所示,
client向leader發出請求,leader首先在本地的log中增加一條記錄,同時發出append請求給其它所有的follower,follower收到請求后在自己的log中追加一條記錄并回復leader,當leader確定有超過一半的server記錄該操作后,就會把本次操作提交到狀態機。
來看一下Raft日志的組成形式,log由一串連續的記錄組成,每條記錄是一個三元組(index,term,command)。其中,index是指記錄在日志中的位置,term是該條記錄在追加時的任期,command是每一次具體的操作信息。
當日志發生不一致的時候,leader和follower之間就需要進行日志比較,為了快速完成這樣的操作,Raft定義了日志匹配特性可以達到此目的,
- 如果不同日志中的兩個記錄有相同的index和term,則它們存儲的操作是相同的;
- 如果不同日志中的兩條記錄有相同的index和term,則所有以前的條目中的記錄都是相同的。
對于一般正常的操作,leader和follower的日志會一直保持一致,但是每臺機器都不是100%高可用的,leader宕機的情況下leader和follower的日志就及其容易造成不一致。
安全性
為了保證Raft算法的安全性,還需要兩個特殊的限制,接下來我們就來看看這兩個限制是什么?
選舉限制
任何leader-based一致性算法中leader必須保存所有的提交的日志記錄,一些一致性算法像VR,Zookeeper等在server中不包含所有committed日志的情況下依然可以被選擇為leader。事物都有其兩面性,這樣看似優雅的處理讓協議的實現變得復雜困難。相反,Raft使用簡單的策略保證被選舉的leader包含所有已經提交的日志。策略就是,選舉leader的時候總是選擇擁有最新日志的server作為leader。比較方法就是利用上面提到的日志匹配特性:
- 當term不同時,term更大的log更新
- 當term相同時,選擇擁有更大index的
日志提交限制
當有半數以上的server保存了日志,leader可以安全的提交。有一種情況就是leader崩潰前并沒有提交日志,未來的leader會繼續未完成的使命。然而,新的leader并不知道日志記錄是否已經同步到半數以上的server,況且,即便半數以上的server已經同步過了,依然有可能被覆蓋掉。為了避免這種情況的發生,Raft要求leader永遠不通過統計副本數量的方式提交先前term的日志,統計副本的方式只適用于當前term日志。當新的term有日志提交后,先前term的日志間接也會被提交,這就保證了同樣index的日志不會被提交兩次。
總結
相比較于Paxos協議,Raft在設計之初就本著簡單易用的宗旨,才成就了這樣一個可理解的,易于實現的,對于人類思維毫無違和感的協議。
參考
[1]. Leslie Lamport. Paxos Made Simple. 2001
[2]. Diego Ongaro and John Ousterhout. Raft Paper. 2013
[3]. Raft Website. The Raft Consensus Algorithm
[4]. Raft Demo. Raft Animate Demo