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