1. 程式人生 > >Zookeeper的一致性協議:Zab

Zookeeper的一致性協議:Zab

支持 源碼 proc 源碼實現 multipl 客戶端連接 red 使用 補充

Zookeeper使用了一種稱為Zab(Zookeeper Atomic Broadcast)的協議作為其一致性復制的核心,據其作者說這是一種新發算法,其特點是充分考慮了Yahoo的具體情況:高吞吐量、低延遲、健壯、簡單,但不過分要求其擴展性。下面將展示一些該協議的核心內容:

另,本文僅討論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的時機點分幾種情況:
  1. 老Leader在COMMIT前Crash(已經提交到本地)
  2. 老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 figure out which value has been committed.
Paxos算法的確是不關系請求之間的邏輯順序,而只考慮數據之間的全序,但很少有人直接使用paxos算法,都會經過一定的簡化、優化。 一般Paxos都會有幾種簡化形式,其中之一便是,在存在Leader的情況下,可以簡化為1個階段(Phase2)。僅有一個階段的場景需要有一個健壯的Leader,因此工作重點就變為Leader選舉,在考慮到Learner的過程,還需要一個”學習“的階段,通過這種方式,Paxos可簡化為兩個階段:
  • 之前的Phase2
  • Learn
如果再考慮多數派要Learn成功,這其實就是Zab協議。Paxos算法著重是強調了選舉過程的控制,對決議學習考慮的不多,Zab恰好對此進行了補充。 之前有人說,所有分布式算法都是Paxos的簡化形式,雖然很絕對,但對很多情況的確如此,但不知Zab的作者是否認同這種說法?

4.結束

本文只是想從協議、算法的角度分析Zookeeper,而非分析其源碼實現,因為Zookeeper版本的變化,文中描述的場景或許已找不到對應的實現。另,本文還試圖揭露一個事實:Zab就是Paxos的一種簡化形式。 【參考資料】
  • A simple totally ordered broadcast protocol
  • paxos

http://blog.csdn.net/chen77716/article/details/7309915

Zookeeper的一致性協議:Zab