——轉自:{老碼農的專欄}算法
前兩篇Paxos算法的討論,讓咱們對paxos算法的理論造成過程有了大概的瞭解,但距離其成爲一個可執行的算法程序還有很長的路要走,緣由是不少的細節和錯誤未被考慮。Google Chubby的做者說,paxos算法實現起來遠沒有看起來簡單,緣由是paxos的容錯僅限於server crash這一種狀況,但在實際工程實現時要考慮磁盤損壞、文件損壞、Leader身份丟失等諸多的錯誤。編程
1. Paxos各角色的職能promise
在paxos算法中存在Client、Proposer、Proposer Leaer、Acceptor、Learn五種角色,可精簡爲三種主要角色:proposer、acceptor、learn。角色只是邏輯上存在的,在實際實現中,節點能夠身兼多職。網絡
在咱們的討論中,咱們先假定沒有Proposer Leader這一角色,在不考慮活鎖的狀況下,若是算法過程正確,那有Leader角色的算法過程確定也正確。分佈式
除了五種角色,還有三個重要的概念:instance、proposal、value,分別表明:每次paxos選舉過程、提案、提案的valueoop
固然,還有4個關鍵過程:學習
對acceptor來講,還蘊含是着promise、accept、reject三個動做。google
先上一幅圖,更直觀地對幾種角色的職能加以瞭解(各角色的具體職能參考Lamport的論文就足夠了):spa
上圖不是很是嚴格,僅爲表現各角色之間的關係。.net
2. Proposer
在Proposer、Acceptor、Learn中均涉及到proposal的編號,該編號應該有proposer做出改變,對其餘的角色是隻讀的,這就保證了只有一個數據源。當多個proposer同時提交proposal時,必須保證各proposer的編號惟1、且可比較,具體作法以前已經提到。這裏還要強調一點的是,僅每一個proposer按本身的規則提升編號是不夠的,還必須瞭解「外面」的最大編號是多少,例如,P一、P二、P3(請參考:Paxos算法2#再論編號問題:編號惟一性)
這要求acceptor要在reject消息中給出當前的最大編號,proposer可能出現宕機,重啓後繼續服務,reject消息會幫助它迅速找到下一個正確編號。可是當多個acceptor回覆各自不一的reject消息時,事情就變得複雜起來。
當proposer發送proposal給一個acceptor時,會有三種結果:
在判斷是否能夠進行Phase2時的一個充分條例就是:必須有acceptor的多數派promise了當前的proposal。
下面分別從Phase1和Phase2討論proposer的行爲:
Phase1-prepare:發送prepare到acceptor
Proposer在本地選擇proposal編號,發送給acceptor,會收到幾種狀況的response:
(a). 沒有收到多數派的迴應
消息丟失、Server宕機致使沒有多數派響應,在可靠消息傳輸(TCP)下,應該報告宕機致使剩餘的Server沒法繼續提供服務,在實際中一個多數派同時宕機的可能性很是小。
(b). 收到多數派的reject
Acceptor可能會發生任意的錯誤,好比消息丟失、宕機重啓等,會致使每一個acceptor看到的最大編號不一致,於是在reject消息中response給proposer的最大編號也不一致,這種狀況proposer應該取其最大做爲比較對象,從新計算編號後繼續Phase1的prepare階段。
(c). 收到多數派的promise
根據包含的value不一樣,這些promise又分三種狀況:
所有爲null的狀況比較好處理,只要proposer自由決定value便可;多數派達成一致的狀況也好處理,選擇已經達成決議的value提交便可,value互不相同的狀況有兩種處理方式:
其實問題的本質是:在一個instance內,一個acceptor是否能accept多個value?約束P2只是要求,若是某個value v已被選出,那以後選出的還應該是v;反過來講,若是value v尚未被多數派accept,也沒有限制acceptor只accept一個value。
感受兩種處理方式均可以,只要選擇一個value,能在paxos以後的過程當中達成一致便可。其實否則,有可能value v已經成爲了最終決議,但acceptor不知道,若是這時不選擇value v而選其餘value,會致使在一次instance內達成兩個決議。
會不會存在這樣一種狀況:A、B、C、D爲多數派的promise,A、B、C的proposal編號,value爲(1,1),D的爲(2,2)?就是說,編號互不一致,但小編號的已經達成了最終決議,而大編號的沒有?
設:小編號的proposal爲P1,value爲v1;大編號的proposal爲P2,value爲v2
也就是說,只要按照【方案2】選擇value就能保證結果的正確性。之因此能有這樣的結果,關鍵仍是那個神祕的多數派,這個多數派起了兩個相當重要的做用:
而多數派能起做用的緣由就是,任何兩個多數派至少有一個公共成員,而這個公共成員對後續proposal的行爲起着決定性的影響,若是這個多數派拒絕了後續的proposal,這些proposal就會由於沒法造成新的多數派而進行不下去。這也是paxos算法的精髓所在吧。
Phase2-accept:發送accept給acceptor
若是一切正常,proposer會選擇一個value發送給acceptor,這個過程比較簡單
accept也會收到2種迴應:
(a). acceptor多數派accept了value
一旦多數派accept了value,那最終決議就已達成,剩下的工做就是交由learn學習並關閉本次選舉(instance)。
(b). acceptor多數派reject了value或超時
說明acceptor不可用或提交的編號不夠大,繼續Phase1的處理。
proposer的處理大概如此,但實際編程時還有幾個問題要考慮:
其餘2個問題比較簡單,持久化的問題有必要討論下。
持久化的目的是在proposer server宕機「甦醒」時,能夠繼續參與paxos過程。
從前面分析可看出,proposer工做的正確性是靠編號的正確性來保證的,編號的正確性是由proposer對編號的初始化寫及acceptor的reject一塊兒保證的,因此只要acceptor能正常工做,proposer就無須持久化當前編號。
3. acceptor
acceptor的行爲相對簡單,就是根據提案的編號決定是否接受proposal,判斷編號依賴promise和accept兩種消息,所以acceptor必須對接收到的消息作持久化處理。根據以前的討論也知道,acceptor的持久化也會影響着proposer的正確性。
在acceptor對proposal進行決策的時候,還有個重要的概念沒有被詳細討論,即instance。任何對proposal的判斷都是基於某個instance,即某次paxos過程,當本次instance宣佈結束(選出了最終決議)時,paxos過程就轉移到下一個instance。這樣會衍生出幾個問題:
根據1中對各角色職能的討論,決議是否被選出是由learn來決定的,當learn得知某個value v已經被多數派accept時,就認爲決議被選出,並宣佈關閉當前的instance。與2中提到的同樣,由於網絡緣由acceptor可能不會得知instance已被關閉,而會繼續對proposer回答關於該instance的問題。也就是說,不管如何acceptor都沒法準確得知instance是否關閉,acceptor程序的正確性也就不能依賴instance是否關閉。但acceptor在已經知道instance已被關閉的狀況下,在拒絕proposer時能提供更多的信息,好比,可使proposer選擇一個更高的instance從新提交請求。
固然,只要proposer根據2中提到的方式進行提案,就不會發生同一instance中產生兩個決議的狀況。
4. learn
learn的主要職責是學習決議,但決議必須一個一個按順序學,不能跳號,好比learn已經知道了100,102號決議,必須同時知道101時才能一塊兒學習。只是簡單的等待101號決議的到來顯然不是一個好辦法,learn還要去主動向acceptor詢問101號決議的狀況,acceptor會對消息作持久化,作到這一點顯然不難。
learn還要週期性地check所接收到的value,一旦意識到決議已經達成,必須關閉對應的instance,並通知acceptor、proposer等(根據須要能夠通知任意多的對象)。
learn還存在一個問題是,是選擇一個server作learn仍是選多個,假若有N個acceptor,M個learn,則會產生N*M的通訊量,若是M很大則通訊量會很是大,若是M=1,通訊量小但會造成單點。折中方案是選擇規模相對較小的M,使這些learn通知其餘learn。
paxos中的learn相對比較抽象,好理解但不可思議能作什麼,緣由在於對paxos的應用場景不清晰。通常說來有兩種使用paxos的場景:
若是把paxos做爲單獨的服務,那learn的做用就是達成決議後通知客戶端;若是是應用的一部分,learn則會直接執行業務邏輯,好比開始數據複製。
持久化:
learn所依賴的全部信息就是value和instance,這些信息都已在acceptor中進行了持久化,因此learn不須要再對消息進行持久化,當learn新加入或重啓時要作的就是能經過acceptor把這些信息取回來。
錯誤處理:
learn可能會重啓或新加入後會對「以前發生的事情」不清楚,解決辦法是:使learn繼續監聽消息,直至某個instance對應的value達成一致時,learn再向acceptor請求以前全部的instance。
至此,咱們進一步討論了paxos個角色的職責和可能的實現分析,離咱們把paxos算法變爲一個可執行程序的目標又進了一步,使咱們對paxos的實現方式大體內心有底,但還有諸多的問題須要進一步討論,好比錯誤處理。雖然文中也提到了一些錯誤及處理方式,但尚未系統地考慮到全部的錯誤。
接下來的討論將重點圍繞着分佈式環境下的錯誤處理。