All working protocols for asynchronous consensus we have so far encountered have Paxos at their core.
——Google 的 Chubby[1]
在分布式算法領(lǐng)域,有個(gè)非常重要的算法叫 Paxos。
Paxos 算法是由 Leslie Lamport 提出的,他在《Paxos Made Simple》[2]中寫道:The Paxos algorithm, when presented in plain English, is very simple.
大神 Lamport 的第一篇論文《The Part-Time Parliament》[3]用在虛構(gòu)的希臘島嶼 Paxos 上的人們通過議會(huì)表決法律來解釋 Paxos 算法,群眾紛紛表示太難理解了。大神表示你們這群渣渣不懂我的幽默,既然如此,我就用簡(jiǎn)明英語(yǔ)再表述一遍。Lamport 說非常簡(jiǎn)單,但是 Paxos 公認(rèn)的繁瑣難懂,尤其是要用程序員嚴(yán)謹(jǐn)?shù)乃季S將所有細(xì)節(jié)理清的時(shí)候,你更多的是下面的表情:
本文核心目的是讓與筆者一樣的小白理清下述三個(gè)問題:
1、Paxos 究竟在解決什么問題?
2、Paxos 算法的核心思想是什么?/3、Paxos 的2階段都做了什么?
Paxos 是什么
分布式系統(tǒng)中的節(jié)點(diǎn)通信存在兩種模型:共享內(nèi)存(Shared memory)和消息傳遞(Messages passing)。
基于消息傳遞通信模型的分布式系統(tǒng),不可避免的會(huì)發(fā)生以下錯(cuò)誤:
進(jìn)程可能會(huì)慢、被殺死或者重啟;
消息可能會(huì)延遲、丟失、重復(fù);
在基礎(chǔ) Paxos 場(chǎng)景中,先不考慮可能出現(xiàn)消息篡改即拜占庭錯(cuò)誤的情況;
……
Paxos 算法解決的問題是在一個(gè)可能發(fā)生上述異常的分布式系統(tǒng)中如何就某個(gè)值達(dá)成一致,保證不論發(fā)生以上任何異常,都不會(huì)破壞決議的一致性。
——維基百科
簡(jiǎn)而言之: Paxos 的目的是讓整個(gè)集群的結(jié)點(diǎn)對(duì)某個(gè)值的變更達(dá)成一致。Paxos 可以說是一個(gè)民主選舉的算法——大多數(shù)節(jié)點(diǎn)的決定會(huì)成個(gè)整個(gè)集群的統(tǒng)一決定。任何一個(gè)點(diǎn)都可以提出要修改某個(gè)數(shù)據(jù)的提案,是否通過這個(gè)提案取決于這個(gè)集群中是否有超過半數(shù)的節(jié)點(diǎn)同意。取值一旦確定將不再更改,并且可以被獲取到(不可變性,可讀取性)。
Paxos 各角色的職能
Proposer:提議者,提出議案(同時(shí)存在一個(gè)或者多個(gè),他們各自發(fā)出提案);
Acceptor:接受者,收到議案后選擇是否接受;
Client:產(chǎn)生議題者,發(fā)起新的請(qǐng)求;
Learner:最終決策學(xué)習(xí)者,只學(xué)習(xí)正確的決議。
上面4種角色中最主要的是 Proposer 和 Acceptor。Proposer 就像 Client 的使者,由 Proposer 使者拿著 Client 的議題去向 Acceptor 提議,讓 Acceptor 來決策。主要的交互過程在 Proposer 和 Acceptor 之間。
下面用一幅圖來標(biāo)識(shí)角色之間的關(guān)系。
上圖中是畫了很多節(jié)點(diǎn)的,每個(gè)節(jié)點(diǎn)需要一臺(tái)機(jī)器么?答案是不需要的。上面的圖是邏輯圖,物理中,可以將 Acceptor 和 Proposer 以及 Client 放在一臺(tái)機(jī)器上,Acceptor 啟動(dòng)端口進(jìn)行 TCP 監(jiān)聽,Proposer 來主動(dòng)連接即可。所以完全可以將 Client、Proposer、Acceptor、Learner 合并到一個(gè)程序里面。
Paxos 算法內(nèi)容
我們先看看 Paxos 在原作者的《Paxos Made Simple》中的描述。
決議的提出與批準(zhǔn)
通過一個(gè)決議分為兩個(gè)階段:
1)prepare 階段
proposer 選擇一個(gè)提案編號(hào) n 并將 prepare 請(qǐng)求發(fā)送給 acceptors 中的一個(gè)多數(shù)派;
acceptor 收到 prepare 消息后,如果提案的編號(hào)大于它已經(jīng)回復(fù)的所有 prepare 消息,則 acceptor 將自己上次接受的提案回復(fù)給 proposer,并承諾不再回復(fù)小于 n 的提案;
下圖是一個(gè) proposer 和5個(gè) acceptor 之間的交互,對(duì)2種不同的情況做了處理。
2)批準(zhǔn)階段
當(dāng)一個(gè) proposer 收到了多數(shù) acceptors 對(duì) prepare 的回復(fù)后,就進(jìn)入批準(zhǔn)階段。它要向回復(fù) prepare 請(qǐng)求的 acceptors 發(fā)送 accept 請(qǐng)求,包括編號(hào) n 和 value;
在不違背自己向其他 proposer 的承諾的前提下,acceptor 收到 accept 請(qǐng)求后即接受這個(gè)請(qǐng)求。
可以看出,Proposer 與 Acceptor 之間的交互主要有4類消息通信,這4類消息對(duì)應(yīng)于 paxos 算法的兩個(gè)階段4個(gè)過程。用2輪 RPC 來確定一個(gè)值。上面的圖解都只是一個(gè) Proposer,但是實(shí)際中肯定是有其他 Proposer 針對(duì)同一件事情發(fā)出請(qǐng)求,所以在每個(gè)過程中都會(huì)有些特殊情況處理,這也是為了達(dá)成一致性所做的事情。
如果在整個(gè)過程中沒有其他 Proposer 來競(jìng)爭(zhēng),那么這個(gè)操作的結(jié)果就是確定無(wú)異議的。但是如果有其他 Proposer 的話,情況就不一樣了。
唯一編號(hào)
保證 Paxos 正確運(yùn)行的一個(gè)重要因素就是提案(proposal)編號(hào),編號(hào)之間要能比較大小/先后,如果是一個(gè) proposer 很容易做到,如果是多個(gè) proposer 同時(shí)提案,該如何處理?Lamport 不關(guān)心這個(gè)問題,只是要求編號(hào)必須是全序的,但我們必須關(guān)心。這個(gè)問題看似簡(jiǎn)單,實(shí)際還稍微有點(diǎn)棘手,因?yàn)檫@本質(zhì)上是也是一個(gè)分布式的問題。
在 Google 的 Chubby 論文中給出了這樣一種方法:
假設(shè)有 n 個(gè) proposer,每個(gè)編號(hào)為 ir (0 <= ir < n),proposol 編號(hào)的任何值 s 都應(yīng)該大于它已知的最大值,并且滿足:s % n = ir => s = m * n + ir
proposer 已知的最大值來自兩部分:proposer 自己對(duì)編號(hào)自增后的值和接收到 acceptor 的 reject 后所得到的值。
我們以3個(gè) proposer P1、P2、P3為例。
開始m=0,編號(hào)分別為0,1,2。 P1提交的時(shí)候發(fā)現(xiàn)了P2已經(jīng)提交,P2編號(hào)為1 > P1的0,因此P1重新計(jì)算編號(hào):new P1 = 1 \* 3 + 0 = 4
P3以編號(hào)2提交,發(fā)現(xiàn)小于P1的4,因此P3重新編號(hào):new P3 = 1 * 3 + 2 = 5
整個(gè) paxos 算法基本上就是圍繞著 proposal 編號(hào)在進(jìn)行:proposer 忙于選擇更大的編號(hào)提 交proposal,acceptor 則比較提交的 proposal 的編號(hào)是否已是最大,只要編號(hào)確定了,所對(duì)應(yīng)的 value 也就確定了。所以說,在 paxos 算法中沒有什么比 proposal 的編號(hào)更重要。
Multi Paxos
Paxos 對(duì)某一個(gè)問題達(dá)成一致的一個(gè)協(xié)議。《Paxos Made Simple》[2]花大部分的時(shí)間解釋的就是這個(gè)一個(gè)提案的問題,然后在結(jié)尾的 Implementing a State Machine ?章節(jié)介紹了我們大部分的應(yīng)用場(chǎng)景是對(duì)一堆連續(xù)的問題達(dá)成一致。
最簡(jiǎn)單的方法就是實(shí)現(xiàn)每一個(gè)問題獨(dú)立運(yùn)行一個(gè) Paxos 的過程(instance)。每個(gè)過程都是獨(dú)立的,相互不會(huì)干擾,這樣可以為一組連續(xù)的問題達(dá)成一致。但是這樣每一個(gè)問題都需要 Prepare, Accept 兩個(gè)階段才能夠完成。Prepare 階段頻繁請(qǐng)求會(huì)造成無(wú)謂的浪費(fèi),我們能不能把這個(gè)過程給減少。
這樣就引入 Proposer Leader 的選舉,正常的 Paxos 二階段從 Proposer Group 中選舉出 Leader 后,后續(xù)統(tǒng)一由 Leader 發(fā)起提案,只有 Leader 才能發(fā)起提案的話相當(dāng)于 Proposer 只有一個(gè),所以可以省略 Prepare 階段直接進(jìn)入到 Accpet 階段。直至發(fā)生 Leader 宕機(jī)、重新進(jìn)行選舉。
《Paxos Made Live》[5]論文中講解了如何使用 multi paxos 實(shí)現(xiàn) chubby 的過程,以及實(shí)現(xiàn)過程中需要解決的問題,比如需要解決磁盤沖突,如何優(yōu)化讀請(qǐng)求,引入了 Epoch number 等。
實(shí)際應(yīng)用
從上面我們知道,Paxos 在節(jié)點(diǎn)宕機(jī)恢復(fù)、消息無(wú)序或丟失、網(wǎng)絡(luò)分化的場(chǎng)景下能保證決議的一致性。而 Paxos 的描述側(cè)重于理論,在實(shí)際項(xiàng)目應(yīng)用中,處理了 N 多實(shí)際細(xì)節(jié)后,可能已經(jīng)變成了另外一種算法,這時(shí)候正確性已經(jīng)無(wú)法得到理論的保證。
要證明分布式一致性算法的正確性通常比實(shí)現(xiàn)算法還困難。所以很多系統(tǒng)實(shí)際中使用的都是以 Paxos 理論為基礎(chǔ)而衍生出來的變種和簡(jiǎn)化版。例如 Google 的 Chubby、MegaStore、Spanner 等系統(tǒng),ZooKeeper 的 ZAB 協(xié)議,還有更加容易理解的 raft 協(xié)議。大部分系統(tǒng)都是靠在實(shí)踐中運(yùn)行很長(zhǎng)一段時(shí)間才能謹(jǐn)慎的表示,系統(tǒng)已基本運(yùn)行,沒有發(fā)現(xiàn)大的問題。
參考文獻(xiàn):
The Chubby lock service for loosely-coupled distributed systems. Mike Burrows, Google Inc ---- Google Chubby論文
Paxos Made Simple. Leslie Lamport ---- 2001年
注:Lamport覺得同行無(wú)法接受他的幽默感,于是用容易接受的方法重新表述了一遍。
The Part-Time Parliament. Leslie Lamport ---- Lamport于1998年發(fā)表在ACM Transactions on Computer Systems。
注:這是該算法第一次公開發(fā)表。
Paxos算法維基百科 ---- 里面詳細(xì)的描述了Paxos的論證過程
Paxos Made Live - An Engineering Perspective ---- Multi Paxos實(shí)際應(yīng)用
Lamport-Paxos ---- 描寫了他用9年時(shí)間發(fā)表這個(gè)算法的前前后后
注:部分圖片來自網(wǎng)絡(luò)
本文作者:劉作為(點(diǎn)融黑幫),高級(jí)開發(fā)工程師。有5年P(guān)ython后端開發(fā)經(jīng)驗(yàn)、專注于爬蟲、數(shù)據(jù)挖掘與分析。