區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法

區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法

1、CFT簡介

CFT(Crash Fault Tolerance),即故障容錯,是非拜占庭問題的容錯技術。
Paxos 問題是指分佈式的系統中存在故障(crash fault),但不存在惡意(corrupt)節點的場景(便可能消息丟失或重複,但無錯誤消息)下的共識達成問題,是分佈式共識領域最爲常見的問題。最先由Leslie Lamport用 Paxon 島的故事模型來進行描述而得以命名。解決Paxos問題的算法主要有Paxos系列算法和Raft算法,Paxos算法和Raft算法都屬於強一致性算法。node

2、Paxos算法

一、Paxos算法產生背景

在常見的分佈式系統中,總會發生諸如機器宕機或網絡異常(包括消息的延遲、丟失、重複、亂序,網絡分區)等狀況。Paxos算法須要解決的問題就是如何在一個可能發生異常的分佈式系統中,快速且正確地在集羣內部對某個數據的值達成一致,而且保證不論發生任何異常,都不會破壞整個系統的一致性。
區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
Paxos算法用於解決分佈式系統的一致性問題。git

二、Paxos算法簡介

1990年,Leslie Lamport 在論文《The Part-time Parliament》中提出了Paxos共識算法,在工程角度實現了一種最大化保障分佈式系統一致性(存在極小的機率沒法實現一致)的機制。Leslie Lamport做爲分佈式系統領域的早期研究者,由於相關成果得到了2013年度圖靈獎。
Leslie Lamport在論文中將Paxos問題表述以下:
希臘島嶼Paxon上的執法者在議會大廳中表決經過法律(一次Paxos過程),並經過服務員(proposer)傳遞紙條的方式交流信息,每一個執法者會將經過的法律記錄在本身的帳目上。問題在於執法者和服務員都不可靠,他們隨時會由於各類事情離開議會大廳(服務器拓機或網絡斷開),並隨時可能有新的執法者進入議會大廳進行法律表決(新加入機器),使用何種方式可以使得表決過程正常進行,且經過的法律不發生矛盾(對一個值達成一致)。 
Paxos過程當中不存在拜占庭將軍問題(消息不會被篡改)和兩將軍問題(信道可靠)。
Paxos是首個獲得證實並被普遍應用的共識算法,其原理相似兩階段提交算法,進行了泛化和擴展,經過消息傳遞來逐步消除系統中的不肯定狀態。
做爲後續不少共識算法(如 Raft、ZAB等)的基礎,Paxos算法基本思想並不複雜,但最初論文中描述比較難懂,甚至連發表也幾經波折。2001年,Leslie Lamport專門發表論文《Paxos Made Simple》進行從新解釋,其對Paxos算法的描述以下:
Phase1
(a) A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors.
(b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered pro-posal (if any) that it has accepted.
Phase 2
(a) If the proposer receives a response to its prepare requests (numbered n) from a majority of acceptors, then it sends an accept request to each of those acceptors for a proposal numbered n with a value v , where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals.
(b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n.
Paxos算法目前在Google的Chubby、MegaStore、Spanner等系統中獲得了應用,Hadoop中的ZooKeeper也使用了Paxos算法,但使用的算法是原始Paxos算法的改進算法。一般以Paxos算法爲基礎,在實現過程當中處理實際應用場景中的具體細節,能夠獲得一個Paxos改進算法。github

三、Paxos算法原理

Paxos算法的基本思路相似兩階段提交:多個提案者先要爭取到提案的權利(獲得大多數接受者的支持);成功的提案者發送提案給全部人進行確認,獲得大部分人確認的提案成爲批准的結案。
Paxos協議有三種角色:Proposer(提議者),Acceptor(決策者),Learner(決策學習者)。
Paxos 是一個兩階段的通訊協議,Paxos算法的基本流程以下:
區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
第一階段Prepare:
A、Proposer生成一個全局惟一的提案編號N,而後向全部Acceptor發送編號爲N的Prepare請求。
B、若是一個Acceptor收到一個編號爲N的Prepare請求,且N大於本Acceptor已經響應過的全部Prepare請求的編號,那麼本Acceptor就會將其已經接受過的編號最大的提案(若是有的話)做爲響應反饋給Proposer,同時本Acceptor承諾再也不接受任何編號小於N的提案。
第二階段 Accept
A、若是Proposer收到半數以上Acceptor對其發出的編號爲N的Prepare請求的響應,那麼Proposer就會發送一個針對[N,V]提案的Accept請求給半數以上的Acceptor。V就是收到的響應中編號最大的提案的value,若是響應中不包含任何提案,那麼V由Proposer本身決定。
B、若是Acceptor收到一個針對編號爲N的提案的Accept請求,只要該Acceptor沒有對編號大於N的Prepare請求作出過響應,Acceptor就接受該提案。
Paxos 並不保證系統總處在一致的狀態。但因爲每次達成共識至少有超過一半的節點參與,最終整個系統都會獲知共識結果。若是提案者在提案過程當中出現故障,能夠經過超時機制來緩解。
Paxos 能保證在超過一半的節點正常工做時,系統總能以較大機率達成共識。算法

四、提案ID的生成算法

對於提案ID的選擇,《Paxos made simple》中提到的是讓全部的Proposer都從不相交的數據集合中進行選擇。
Google的Chubby論文中給出提案ID的生成算法以下:假設有n個Proposer,每一個編號爲promise

ir(0<=ir<n)

,Proposal編號的任何值s都應該大於它已知的最大值,而且知足:安全

s %n = ir    =>     s = m*n + ir

    Proposer已知的最大值來自兩部分:Proposer本身對編號自增後的值和接收到Acceptor的拒絕後所獲得的值。
以3個Proposer P一、P二、P3爲例,開始m=0,編號分別爲0,1,2。
1) P1提交的時候發現了P2已經提交,P2編號爲1 >P1的0,所以P1從新計算編號:new P1 = 1*3+1 = 4
2) P3以編號2提交,發現小於P1的4,所以P3從新編號:new P3 = 1*3+2 = 5服務器

五、Paxos算法的活鎖

若是兩個提案者剛好依次提出更新的提案,則致使活鎖,系統會永遠沒法達成共識(實際發生機率很小)。活鎖沒有產生阻塞,可是一直沒法達成一致。
活鎖有三種解決方案:
A、在被打回第一階段再次發起PrepareRequest請求前加入隨機等待時間。
B、設置一個超時時間,到達超時時間後,再也不接收PrepareRequest請求。
C、在Proposer中選舉出一個leader,經過leader統一發出PrepareRequest和AcceptRequest。微信

六、Paxos算法異常處理

Paxos算法在執行過程當中會產生不少的異常狀況:Proposer宕機,Acceptor在接收Proposal後宕機,Proposer接收消息後宕機,Acceptor在Accept後宕機,Learner宕機,存儲失敗等等。
爲保證Paxos算法的正確性,Proposer、Aceptor、Learner都須要實現持久存儲,以作到Server恢復後仍能正確參與Paxos處理。
    Proposer存儲已提交的最大proposal編號、決議編號(instance id)。
    Acceptor存儲已承諾(promise)的最大編號、已接受(Accept)的最大編號和Value、決議編號。
    Learner存儲已學習過的決議和編號。網絡

七、Paxos算法應用

Paxos算法只有兩種狀況下服務不可用:一是超過半數的Proposer異常,二是出現活鎖。前者能夠經過增長Proposer的個數來下降因爲Proposer異常影響服務的機率,後者自己發生的機率就極低。
Paxos是分佈式系統一致性協議的基礎,其它的協議(raft、zab等)都是Paxos協議的改進版本。Paxos側重理論,實現Paxos很是困難。
微信後臺生產級Paxos類庫PhxPaxos實現:
https://github.com/Tencent/paxosstore
https://github.com/tencent-wechat/phxpaxos
基於Paxos算法的改進算法的資料集合:
https://github.com/dgryski/awesome-consensusless

3、三軍問題

一、三軍問題簡介

三軍問題的描述以下:
1) 1支紅軍在山谷裏紮營,在周圍的山坡上駐紮着3支藍軍;
2) 紅軍比任意1支藍軍都要強大;若是1支藍軍單獨做戰,紅軍勝;若是2支或以上藍軍同時進攻,藍軍勝;
3) 三支藍軍須要同步他們的進攻時間;但他們唯一的通訊媒介是派通訊兵步行進入山谷,在那裏他們可能被俘虜,從而將信息丟失;或者爲了不被俘虜,可能在山谷停留很長時間;
4) 每支軍隊有1個參謀負責提議進攻時間;每支軍隊也有1個將軍批准參謀提出的進攻時間;很明顯,1個參謀提出的進攻時間須要得到至少2個將軍的批准纔有意義;
5) 問題:是否存在一個協議,可以使得藍軍同步他們的進攻時間?
三軍問題符合Paxos問題場景,參謀和將軍須要遵循一些基本的規則:
1) 參謀以兩階段提交(prepare/commit)的方式來發起提議,在Prepare階段須要給出一個提議編號;
2) 在Prepare階段產生衝突,將軍以提議編號大小來裁決,提議編號大的參謀勝出;
3) 參謀在Prepare階段若是收到將軍返回的已接受進攻時間,在Commit階段必須使用返回的進攻時間;

二、兩參謀前後提議場景

區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
A、參謀1發起提議,派通訊兵帶信給3個將軍,內容爲(編號1);
B、3個將軍收到參謀1的提議,因爲以前尚未保存任何編號,所以把(編號1)保存下來,避免遺忘;同時讓通訊兵帶信回去,內容爲(ok);
C、參謀1收到至少2個將軍的回覆,再次派通訊兵帶信給3個將軍,內容爲(編號1,進攻時間1);
D、3個將軍收到參謀1的時間,把(編號1,進攻時間1)保存下來,避免遺忘;同時讓通訊兵帶信回去,內容爲(Accepted);
E、參謀1收到至少2個將軍的(Accepted)內容,確認進攻時間已經被你們接收;
F、參謀2發起提議,派通訊兵帶信給3個將軍,內容爲(編號2);
G、3個將軍收到參謀2的提議,因爲(編號2)比(編號1)大,所以把(編號2)保存下來,避免遺忘;又因爲以前已經接受參謀1的提議,所以讓通訊兵帶信回去,內容爲(編號1,進攻時間1);
H、參謀2收到至少2個將軍的回覆,因爲回覆中帶來了已接受的參謀1的提議內容,參謀2所以再也不提出新的進攻時間,接受參謀1提出的時間;

三、兩參謀交叉提議場景

區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
1) 參謀1發起提議,派通訊兵帶信給3個將軍,內容爲(編號1);
2) 3個將軍的狀況以下
A、將軍1和將軍2收到參謀1的提議,將軍1和將軍2把(編號1)記錄下來,若是有其餘參謀提出更小的編號,將被拒絕;同時讓通訊兵帶信回去,內容爲(ok);
B、負責通知將軍3的通訊兵被抓,所以將軍3沒收到參謀1的提議;
3) 參謀2在同一時間也發起了提議,派通訊兵帶信給3個將軍,內容爲(編號2);
4) 3個將軍的狀況以下
A、將軍2和將軍3收到參謀2的提議,將軍2和將軍3把(編號2)記錄下來,若是有其餘參謀提出更小的編號,將被拒絕;同時讓通訊兵帶信回去,內容爲(ok);
B、負責通知將軍1的通訊兵被抓,所以將軍1沒收到參謀2的提議;
5) 參謀1收到至少2個將軍的回覆,再次派通訊兵帶信給有答覆的2個將軍,內容爲(編號1,進攻時間1);
6) 2個將軍的狀況以下
A、將軍1收到了(編號1,進攻時間1),和本身保存的編號相同,所以把(編號1,進攻時間1)保存下來;同時讓通訊兵帶信回去,內容爲(Accepted);
B、將軍2收到了(編號1,進攻時間1),因爲(編號1)小於已經保存的(編號2),所以讓通訊兵帶信回去,內容爲(Rejected,編號2);
7) 參謀2收到至少2個將軍的回覆,再次派通訊兵帶信給有答覆的2個將軍,內容爲(編號2,進攻時間2);
8) 將軍2和將軍3收到了(編號2,進攻時間2),和本身保存的編號相同,所以把(編號2,進攻時間2)保存下來,同時讓通訊兵帶信回去,內容爲(Accepted);
9) 參謀2收到至少2個將軍的(Accepted)內容,確認進攻時間已經被多數派接受;
10) 參謀1只收到了1個將軍的(Accepted)內容,同時收到一個(Rejected,編號2);參謀1從新發起提議,派通訊兵帶信給3個將軍,內容爲(編號3);
11) 3個將軍的狀況以下
A、將軍1收到參謀1的提議,因爲(編號3)大於以前保存的(編號1),所以把(編號3)保存下來;因爲將軍1已經接受參謀1前一次的提議,所以讓通訊兵帶信回去,內容爲(編號1,進攻時間1);
B、將軍2收到參謀1的提議,因爲(編號3)大於以前保存的(編號2),所以把(編號3)保存下來;因爲將軍2已經接受參謀2的提議,所以讓通訊兵帶信回去,內容爲(編號2,進攻時間2);
C、 負責通知將軍3的通訊兵被抓,所以將軍3沒收到參謀1的提議;
12) 參謀1收到了至少2個將軍的回覆,比較兩個回覆的編號大小,選擇大編號對應的進攻時間做爲最新的提議;參謀1再次派通訊兵帶信給有答覆的2個將軍,內容爲(編號3,進攻時間2);
13) 將軍1和將軍2收到了(編號3,進攻時間2),和本身保存的編號相同,所以保存(編號3,進攻時間2),同時讓通訊兵帶信回去,內容爲(Accepted);
14) 參謀1收到了至少2個將軍的(accepted)內容,確認進攻時間已經被多數派接受;

4、Multi-Paxos算法

一、Multi-Paxos算法簡介

Paxos是對一個值達成一致,Multi-Paxos是連續多個Paxos instance來對多個值達成一致。Multi-Paxos協議中有一個Leader。Leader是系統中惟一的Proposal,在lease租約週期內全部提案都有相同的ProposalId,能夠跳過prepare階段,議案只有accept過程,一個ProposalId能夠對應多個Value,因此稱爲Multi-Paxos。
Multi-Paxos協議是經典的Paxos協議的簡化版本,將原來2-Phase過程簡化爲了1-Phase,從而加快了提交速度。Multi-Paxos要求在各個Proposer中有惟一的Leader,並由Leader惟一地提交value給各Acceptor進行表決,在系統中僅有一個Leader進行value提交的狀況下,Prepare的過程就能夠被跳過,而Leader的選舉則能夠由Paxos Lease來完成。

二、PaxosLease

在Paxos算法中,若是可以選舉出一個leader,那麼有助於提升投票的成功率。另外leader在多個決議的選舉中有很重要的做用(用於獲得決議的連續id)。所以,如何經過某種方法獲得一個leader就是PaxosLease所說明的。
Master選舉的過程以下:從衆多的Node中選擇一個做爲Master,若是該Master 一直 alive則無需選舉,若是master crash,則其餘的node進行選舉下一個master。選擇正確性的保證是:任什麼時候刻最多隻能有一個master。
邏輯上Master更像一把無形的鎖,任何一個節點拿到這個鎖,均可成爲master,因此本質上Master選舉是個分佈式鎖的問題,但徹底靠鎖來解決選舉問題也是有風險的:若是一個Node拿到鎖,而後crash,會致使鎖沒法釋放,即死鎖。一種可行的方案是給鎖加個時間(Lease),拿到鎖的Master只能在Lease有效期內訪問鎖定的資源,在Lease timeout後,其餘Node能夠繼續競爭鎖,從根本上避免了死鎖。
Master在拿到鎖後,若是一直alive,能夠向其餘node」續租「鎖,從而避免頻繁的選舉活動。

5、Raft算法

一、Raft算法簡介

Paxos 算法的設計並無考慮到一些優化機制,同時論文中也沒有給出太多實現細節,所以後來出現了很多性能更優化的算法和實現,包括Fast Paxos、Multi-Paxos 等。
Raft算法由斯坦福大學的Diego Ongaro和John Ousterhout 2013年在論文《In Search of an Understandable Consensus Algorithm》中提出。Raft算法面向對多個決策達成一致的問題,是對Multi-Paxos的從新簡化設計和實現,分解了領導者選舉、日誌複製和安全方面的考慮,並經過約束減小了不肯定性的狀態空間。
Raft算法將一致性問題分解爲領導選舉(leader election)、日誌複製(log replication)、安全性(safety)三部分。

二、Raft算法角色

Raft算法包括領導者(Leader)、候選者(Candidate)和跟隨者(Follower)三種角色,決策前經過選舉一個全局的領導者來簡化後續的決策過程。領導者決定日誌(log)的提交,日誌只能由領導者向跟隨者單向複製。
Leader:集羣中只有一個處於Leader狀態的服務器,負責響應全部客戶端的請求。
Follower:剛啓動時全部節點爲Follower狀態,響應Leader的日誌同步請求,響應Candidate的請求。
Candidate:Follower狀態服務器準備發起新的Leader選舉前須要轉換到的狀態,是Follower和Leader的中間狀態。
區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
全部節點初始狀態都是Follower角色
超時時間內沒有收到Leader的請求則轉換爲Candidate進行選舉
Candidate收到大多數節點的選票則轉換爲Leader;發現Leader或者收到更高任期的請求則轉換爲Follower
Leader在收到更高任期的請求後轉換爲Follower

三、Term(任期)

Term(任期):每一個Leader都有本身的任期,任期到期就須要開始新一輪選舉,在每一個任期內,能夠沒有leader,可是不能出現大於兩個的leader。
Raft算法將整個系統執行時間劃分爲若干個不一樣時間間隔長度的Term(任期)構成的序列,以遞增的數字來做爲Term的編號;每一個Term由Election開始,在任期內若干處於Candidate狀態的服務器競爭產生新的Leader,若是一個候選人贏得了選舉,就會在該任期的剩餘時間擔任領導人;在某些狀況下,選票會被瓜分,有可能沒有選出領導人,那麼將會開始另外一個任期,而且馬上開始下一次選舉。Raft算法保證在給定的一個任期最多隻有一個領導人。
區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法

四、Raft算法的流程

Raft算法典型的過程包括兩個主要階段:
(1)Leader選舉
當整個系統啓動時,全部服務器都處於Follower狀態;若是系統中存在Leader,Leader會週期性的發送心跳(AppendEntries RPC)來告訴其它服務器本身是Leader;若是Follower通過一段時間沒有收到任何心跳信息,則能夠認爲Leader不存在,須要進行Leader選舉。
在選舉前,Follower增長其Term編號並改變狀態爲Candidate狀態,而後向集羣內的其它服務器發出RequestVote RPC,Candidate狀態持續到發生下面三個中的任意事件:
A、贏得選舉:Candidate接受了大多數服務器的投票,成爲Leader,而後向其它服務器發送心跳(AppendEntries RPC)告訴其它服務器。
B、其它服務器得到選舉:Candidate在等待的過程當中接收到自稱爲Leader的服務器發送來的心跳(AppendEntries RPC),若是RPC的Term編號大於等於Candidate自身的Term編號,則Candidate認可Leader,自身狀態變成Follower;不然拒絕認可Leader,狀態依然爲Candidate。
C、一段時間事後,若是沒有新的Leader產生,則Term遞增,從新發起選舉(由於有可能同時有多個Follower轉爲Candidate狀態,致使分流,都沒有得到多數票)。
(2)日誌複製
Log複製主要做用是用於保證節點的一致性,所作的操做是爲了保證一致性與高可用性;當Leader選舉出來後便開始負責客戶端的請求,全部請求都必須先通過Leader處理。Leader接受到客戶端請求後,將其追加到Log的尾部,而後向集羣內其它服務器發出AppendEntries RPC,引起其它服務器複製新請求的操做,當大多數服務器複製完後,Leader將請求應用到內部狀態機,並將執行結果返回給客戶端。
區塊鏈快速入門(三)——CFT(非拜占庭容錯)共識算法
每一個Log中的項目包含2個內容:操做命令自己和Term編號;還有一個全局的Log Index來指示Log項目在Log中的順序編號。當大多數服務器在Log中存儲了Log項目,則可認爲Log項目是能夠提交的,上圖中的Log Index爲7前的Log項目均可以提交。
(3)安全性
安全性是用來保證每一個節點都執行相同序列的安全機制,如當某個Follower在當前Leader提交命令時不可用,稍後可能該Follower又會被選舉爲Leader,這時新Leader可能會用新的Log覆蓋先前已提交的Log,這就是致使節點執行不一樣序列;安全性就是用於保證選舉出來的Leader必定包含先前已經提交Log的機制。
爲了達到安全性,Raft算法增長了兩個約束條件:
A、要求只有其Log包含了全部已經提交的操做命令的那些服務器纔有權被選爲Leader。
B、對於新Leader來講,只有它本身已經提交過當前Term的操做命令才被認爲是真正的提交。

五、Raft算法的應用

Raft算法已經被多種語言實現,如Go語言、C++、Python等主流開發語言。
Raft算法原理動畫演示:
http://thesecretlivesofdata.com/raft/
Raft共識算法資源以下:
https://raft.github.io/
Raft算法的Go語言實現:
https://github.com/goraft/raft

相關文章
相關標籤/搜索