另,本文僅討論Zookeeper使用的一致性協議而非討論其源碼實現 算法
Zookeeper的實現是有Client、Server構成,Server端提供了一個一致性複製、存儲服務,Client端會提供一些具體的語義,好比分佈式鎖、選舉算法、分佈式互斥等。從存儲內容來講,Server端更多的是存儲一些數據的狀態,而非數據內容自己,所以Zookeeper能夠做爲一個小文件系統使用。數據狀態的存儲量相對不大,徹底能夠所有加載到內存中,從而極大地消除了通訊延遲。 數據庫
Server能夠Crash後重啓,考慮到容錯性,Server必須「記住」以前的數據狀態,所以數據須要持久化,但吞吐量很高時,磁盤的IO便成爲系統瓶頸,其解決辦法是使用緩存,把隨機寫變爲連續寫。 緩存
考慮到Zookeeper主要操做數據的狀態,爲了保證狀態的一致性,Zookeeper提出了兩個安全屬性(Safety Property) 安全
- 全序(Total order):若是消息a在消息b以前發送,則全部Server應該看到相同的結果
- 因果順序(Causal order):若是消息a在消息b以前發生(a致使了b),並被一塊兒發送,則a始終在b以前被執行。
爲了保證上述兩個安全屬性,Zookeeper使用了TCP協議和Leader。經過使用TCP協議保證了消息的全序特性(先發先到),經過Leader解決了因果順序問題:先到Leader的先執行。由於有了Leader,Zookeeper的架構就變爲:Master-Slave模式,但在該模式中Master(Leader)會Crash,所以,Zookeeper引入了Leader選舉算法,以保證系統的健壯性。概括起來Zookeeper整個工做分兩個階段:
- Atomic Broadcast
- Leader選舉
1. Atomic Broadcast
同一時刻存在一個Leader節點,其餘節點稱爲「Follower」,若是是更新請求,若是客戶端鏈接到Leader節點,則由Leader節點執行其請求;若是鏈接到Follower節點,則需轉發請求到Leader節點執行。但對讀請求,Client能夠直接從Follower上讀取數據,若是須要讀到最新數據,則須要從Leader節點進行,Zookeeper設計的讀寫比例是2:1。
Leader經過一個簡化版的二段提交模式向其餘Follower發送請求,但與二段提交有兩個明顯的不一樣之處:
- 由於只有一個Leader,Leader提交到Follower的請求必定會被接受(沒有其餘Leader干擾)
- 不須要全部的Follower都響應成功,只要一個多數派便可
通俗地說,若是有2f+1個節點,容許f個節點失敗。由於任何兩個多數派必有一個交集,當Leader切換時,經過這些交集節點能夠得到當前系統的最新狀態。若是沒有一個多數派存在(存活節點數小於f+1)則,算法過程結束。但有一個特例:
若是有A、B、C三個節點,A是Leader,若是B Crash,則A、C能正常工做,由於A是Leader,A、C還構成多數派;若是A Crash則沒法繼續工做,由於Leader選舉的多數派沒法構成。
2. Leader Election
Leader選舉主要是依賴Paxos算法,具體算法過程請參考其餘博文,這裏僅考慮Leader選舉帶來的一些問題。Leader選舉遇到的最大問題是,」新老交互「的問題,新Leader是否要繼續老Leader的狀態。這裏要按老Leader Crash的時機點分幾種狀況:
- 老Leader在COMMIT前Crash(已經提交到本地)
- 老Leader在COMMIT後Crash,但有部分Follower接收到了Commit請求
第一種狀況,這些數據只有老Leader本身知道,當老Leader重啓後,須要與新Leader同步並把這些數據從本地刪除,以維持狀態一致。
第二種狀況,新Leader應該能經過一個多數派得到老Leader提交的最新數據
老Leader重啓後,可能還會認爲本身是Leader,可能會繼續發送未完成的請求,從而由於兩個Leader同時存在致使算法過程失敗,解決辦法是把Leader信息加入每條消息的id中,Zookeeper中稱爲zxid,zxid爲一64位數字,高32位爲leader信息又稱爲epoch,每次leader轉換時遞增;低32位爲消息編號,Leader轉換時應該從0從新開始編號。經過zxid,Follower能很容易發現請求是否來自老Leader,從而拒絕老Leader的請求。
由於在老Leader中存在着數據刪除(狀況1),所以Zookeeper的數據存儲要支持補償操做,這也就須要像數據庫同樣記錄log。
3. Zab與Paxos
Zab的做者認爲Zab與paxos並不相同,只因此沒有采用Paxos是由於Paxos保證不了全序順序:
Because multiple leaders can
propose a value for a given instance two problems arise.
First, proposals can conflict. Paxos uses ballots to detect and resolve conflicting proposals.
Second, it is not enough to know that a given instance number has been committed, processes must also be able to figure out which value has been committed.
Paxos算法的確是不關係請求之間的邏輯順序,而只考慮數據之間的全序,但不多有人直接使用paxos算法,都會通過必定的簡化、優化。
通常Paxos都會有幾種簡化形式,其中之一即是,在存在Leader的狀況下,能夠簡化爲1個階段(Phase2)。僅有一個階段的場景須要有一個健壯的Leader,所以工做重點就變爲Leader選舉,在考慮到Learner的過程,還須要一個」學習「的階段,經過這種方式,Paxos可簡化爲兩個階段:
若是再考慮多數派要Learn成功,這其實就是Zab協議。Paxos算法着重是強調了選舉過程的控制,對決議學習考慮的很少,Zab剛好對此進行了補充。
以前有人說,全部分佈式算法都是Paxos的簡化形式,雖然很絕對,但對不少狀況的確如此,但不知Zab的做者是否定同這種說法?
4.結束
本文只是想從協議、算法的角度分析Zookeeper,而非分析其源碼實現,由於Zookeeper版本的變化,文中描述的場景或許已找不到對應的實現。另,本文還試圖揭露一個事實:Zab就是Paxos的一種簡化形式。