分散式協調服務zookeeper
ps.本文為《從Paxos到Zookeeper 分散式一致性原理與實踐》筆記之一
ZooKeeper
-
ZooKeeper曾是Apache Hadoop的一個子專案,是一個典型的分散式資料一致性的解決方案,分散式應用程式可以基於它實現資料釋出/訂閱、負載均衡、命名服務、分散式協調/通知、叢集管理、master選舉、分散式鎖和分散式佇列等。
-
ZooKeeper是Google的Chubby一個開源的實現,由雅虎建立,是Hadoop和Hbase的重要元件。
-
ZooKeeper沒有直接採用paxos演算法,而是採用了一種被稱為ZAB(Zookeeper Atomic Broadcast)的一致性協議
-
ZooKeeper的目標就是封裝好複雜易出錯的關鍵服務,將簡單易用的介面和效能高效、功能穩定的系統提供給使用者。
ZooKeeper可以保證如下分散式一致性特性
-
順序一致性:從同一個客戶端發起的事務請求,最終將會嚴格地按照其發起順序被應用到Zookeeper中;
-
原子性:所有事務的請求結果在整個叢集中所有機器上的應用情況是一致的,也就是說,要麼在整個叢集中所有機器上都成功應用了某一個事務,要麼都沒有應用,沒有中間狀態;
-
單一檢視:無論客戶端連線的是哪個Zookeeper伺服器,其看到的服務端資料模型都是一致的。
-
可靠性:一旦服務端成功應用了一個事務,並完成對客戶端的響應,那麼該事務所引起的服務端狀態變更將會被一直保留下來,除非有另一個事務又對其進行了變更。
-
實時性:Zookeeper僅僅保證在一定的時間內,客戶端最終一定能夠從服務端上讀到最終的資料狀態。
ZooKeeper的四個設計目標
zk致力於提供一個高效能、高可用、且具有嚴格的順序訪問控制能力(主要是寫操作)的分散式協議,最後者能使得zk能實現一些複雜的同步語義。
-
簡單的資料模型:能夠通過一個共享的、樹型結構的名字空間來進行相互協調。這裡是樹形結構的名字空間是指zk伺服器記憶體中的一個數據結構,由其一系列的ZNode資料節點組成,他們的層級關係就像檔案系統的目錄結構,不過zk將其全量資料儲存在記憶體中,以達到高吞吐。
-
可以構建叢集:zk叢集通常由一組機器組成,叢集中的每臺機器都會在記憶體中維護當前的伺服器狀態,並且每臺機器之間都保持著通訊。叢集中只要超過一般的機器可以正常工作,zk就可以對外提供服務。zk客戶端會選擇和叢集中任意一臺機器共同來建立一個tcp連結,如果連線斷開,客戶端會自動連線到服務機器的其他機器。
-
順序訪問:對於來自客戶端的每個更新請求,Zookeeper都會分配一個全域性唯一的遞增編號,這個編號反映了所有事務操作的先後順序。
-
高效能:Zookeeper將全量資料儲存在記憶體中,並直接服務於客戶端的所有非事務請求,因此它尤其適用於以讀操作為主的應用場景。
ZooKeeper的基本概念
-
叢集角色
-
最典型的叢集就是Master/Slave模式(主備模式),此情況下把所有能夠處理寫操作的機器稱為Master機器,把所有通過非同步複製方式獲取最新資料,並提供讀服務的機器為Slave機器。
-
Zookeeper不採用主備模式,它引入了Leader、Follower、Observer三種角色,Zookeeper叢集中的所有機器通過Leaser選舉過程來選定一臺被稱為Leader的機器,Leader伺服器為客戶端提供讀和寫服務,Follower和Observer提供讀服務,但是Observer不參與Leader選舉過程,不參與寫操作的"過半寫成功"策略,Observer可以在不影響寫效能的情況下提升叢集的效能。
-
leader:
是整個叢集工作機制中的核心,其主要工作有: 1、事務請求的唯一排程和處理者,保證叢集事務處理的順序性。 2、叢集內部各伺服器的排程者。 -
follower:
是zookeeper叢集狀態的跟隨者,其主要工作是: 1、處理客戶端的非事務請求,轉發事務請求給leader伺服器。 2、參與事務請求proposal的投票 3、參與leader選舉投票 -
observer
和follower唯一的區別在於,observer伺服器只提供非事務服務,不參與任何形式的投票,包括事務請求proposal的投票和leader選舉投票。 通常在不影響叢集事務處理能力的前提下提升叢集的非事務處理能力。
-
-
會話
- 指客戶端會話,一個客戶端連線是指客戶端和服務端之間的一個TCP長連線,Zookeeper對外的服務埠預設為2181,客戶端啟動的時候,首先會與伺服器建立一個TCP連線,從第一次連線建立開始,客戶端會話的生命週期也開始了,通過這個連線,客戶端能夠心跳檢測與伺服器保持有效的會話,也能夠向Zookeeper伺服器傳送請求並接受響應,同時還能夠通過該連線接受來自伺服器的Watch事件通知。
-
資料節點
- 我們常說的節點指的是叢集中的機器節點,zk中節點有兩類,第一類指構成叢集的機器,稱為機器節點,第二類是指資料模型中的資料單元,稱為資料節點-Znode,Zookeeper將所有資料儲存在記憶體中,資料模型是一棵樹,由斜槓/進行分割的路徑,就是一個ZNode,如/foo/path1,每個ZNode都會儲存自己的資料記憶體,同時還會儲存一些列屬性資訊。
- ZNode分為持久節點和臨時節點兩類,持久節點是指一旦這個ZNode被建立了,除非主動進行ZNode的移除操作,否則這個ZNode將一直儲存在Zookeeper上,而臨時節點的生命週期和客戶端會話繫結,一旦客戶端會話失效,那麼這個客戶端建立的所有臨時節點都會被移除。
- 另外,Zookeeper還允許使用者為每個節點新增一個特殊的屬性:SEQUENTIAL。一旦節點被標記上這個屬性,那麼在這個節點被建立的時候,Zookeeper會自動在其節點後面追加一個整形數字,其是由父節點維護的自增數字。
- 臨時,持久和順序,如此,資料節點一共有四種類型:
- 持久節點
- zk中最常見的一種節點型別。除非主動刪除,否則一直保留
- 持久順序節點
- 基本和持久節點一致,額外的特性表現在順序性上。持久順序節點在建立節點的時候,zk會自動給它的名字加上數字字尾,表示在該父節點下的建立順序,字尾上線是整型最大值。
- 臨時節點
- 臨時節點的生命週期和客戶端會話繫結,一旦客戶端會話失效,那麼這個客戶端建立的所有臨時節點都會被移除。
- 臨時順序節點
- 同臨時節點,再加上順序特性。
- 持久節點
-
stat
- 資料節點中除了有資料內容外,還有一個stat物件來記錄節點的狀態資訊:
-
版本
- 對於每個ZNode,Zookeeper都會為其維護一個叫作Stat的資料結構,Stat記錄了這個ZNode的三個資料版本,分別是version(當前ZNode的版本)、cversion(當前ZNode子節點的版本)、aversion(當前ZNode的ACL版本)。
- 類似於樂觀鎖和cas,保證原子性操作
-
事務操作
- 在ZooKeeper中,能改變ZooKeeper伺服器狀態的操作稱為事務操作。一般包括資料節點建立與刪除、資料內容更新和客戶端會話建立與失效等操作。對應每一個事務請求,ZooKeeper都會為其分配一個全域性唯一的事務ID,用ZXID表示,通常是一個64位的數字。每一個ZXID對應一次更新操作,從這些ZXID中可以間接地識別出ZooKeeper處理這些事務操作請求的全域性順序。
-
watcher
- watcher是事件監聽器,Zookeeper允許使用者在指定節點上註冊一些Watcher,並且在一些特定事件觸發的時候,Zookeeper服務端會將事件通知到感興趣的客戶端。該機制是zk實現分散式協調服務的重要特性。
- 其邏輯如下圖:
- 當客戶端向服務端註冊watcher時,也會將watcher物件儲存在客戶端的watcherManager中,當服務端出發watcher時間後,向客戶端傳送通知,客戶端從watcherManager中去處watcher物件來執行回撥邏輯。
-
ACL
Zookeeper採用ACL(Access Control Lists)策略來進行許可權控制,類似於unix檔案系統的許可權控制,- 許可權模式
- ip模式
- 通過ip地址粒度來進行許可權控制。
- digest
- 類似於username:password形式配置許可權標識來控制,
- world
- 即任何人都可以訪問
- super
- 一種特殊的digest模式,只不過許可權是超級使用者。
- ip模式
- 許可權:
- CREATE:建立子節點的許可權。
- READ:獲取節點資料和子節點列表的許可權。
- WRITE:更新節點資料的許可權。
- DELETE:刪除子節點的許可權。
- ADMIN:設定節點ACL的許可權。
- 值得注意的是,CREATE和READ都是針對子節點的許可權控制。
- 許可權模式
ZAB協議
ZAB並不是一種通用的分散式一致性演算法,它是一種特別為Zookeeper設計的崩潰可恢復的原子訊息廣播演算法。
核心處理方式
- 所有事務請求必須由一個全域性唯一的伺服器來協調處理,這樣的伺服器被稱為leader伺服器,而餘下的其他伺服器則成為follower伺服器。
- leader伺服器負責將一個客戶端事務請求轉換成一個事務proposal,並將該proposal分發給叢集中所有的follower伺服器。之後leader伺服器需要等待所有follower伺服器的反饋,一旦超過半數的follower伺服器進行了正確的反饋後,那麼leader就會再次向所有的follower伺服器分發commit訊息,要求其將前一個proposal進行提交。
協議內容
-
ZAB有兩種基本的模式:崩潰恢復和訊息廣播。
-
當整個服務框架啟動過程中或Leader伺服器出現網路中斷、崩潰退出與重啟等異常情況時,ZAB協議就會進入恢復模式並選舉產生新的Leader伺服器。
-
當選舉產生了新的Leader伺服器,同時叢集中已經有過半的機器與該Leader伺服器完成了狀態同步之後,ZAB協議就會退出恢復模式,那麼整個服務框架就可以進入訊息廣播模式。其中,所謂的狀態同步是指資料同步,用來保證叢集中存在過半的機器能夠與leader伺服器的資料狀態保持一致。
-
Leader選舉演算法不僅僅需要讓Leader自身知道已經被選舉為Leader,同時還需要讓叢集中的所有其他機器也能夠快速地感知到選舉產生的新的Leader伺服器。
-
當一臺同樣遵守ZAB協議的伺服器啟動並加入集群后,如果已經存在leader,那麼它會自覺的找到leader,與其進行資料同步,然後一起參與訊息廣播。
-
如果follower伺服器接收到客戶端的事務請求,那麼他們會將這個事務請求轉發給leader伺服器。
-
當Leader伺服器出現崩潰或者機器重啟、叢集中已經不存在過半的伺服器與Leader伺服器保持正常通訊時,那麼在重新開始新的一輪的原子廣播事務操作之前,所有程序首先會使用崩潰恢復協議來使彼此到達一致狀態,於是整個ZAB流程就會從訊息廣播模式進入到崩潰恢復模式。
-
一個機器要成為leader,要獲得過半機器的支援,而由於每臺機器都可能崩潰,因此整個過程可能出現多個leader,一個機器也可能多次成為leader。
訊息廣播
-
ZAB協議的訊息廣播過程使用原子廣播協議,類似於一個二階段提交過程,針對客戶端的事務請求,Leader伺服器會為其生成對應的事務Proposal,並將其傳送給叢集中其餘所有的機器,然後再分別收集各自的選票,最後進行事務提交。
-
此處ZAB的二階段提交和一般的二階段提交略有不同,ZAB移除了二階段提交中的事務中斷的邏輯,follower伺服器要麼正常反饋,要麼拋棄leader。好處是我們不需要等待所有follower都反饋響應才能提交事務,壞處是叢集無法處理leader崩潰而帶來的資料不一致的問題。後者需要崩潰恢復模式來解決這個問題。
-
整個訊息廣播協議是基於具有FIFO特性的TCP協議來進行網路通訊的,因此能夠很容易保證訊息廣播過程中訊息接受與傳送的順序性。
-
整個訊息廣播過程中,Leader伺服器會為每個事務生成對應的Proposal來進行廣播,並且在廣播事務Proposal之前,Leader伺服器會先為這個Proposal分配一個全域性單調遞增的唯一ID,稱之為事務ID(ZXID),由於ZAB協議需要保證每個訊息嚴格的因果關係,因此必須將每個事務Proposal按照其ZXID的先後順序來進行排序和處理。
-
在廣播過程中,leader會為每一個follower分配一個單獨的佇列,然後將需要廣播的事務proposal依次放入,並且根據FIFO策略進行訊息傳送。每個follower接收到proposal之後,都會首先將其以事務日誌的形式寫入本地磁碟,寫入成功後反饋leader一個ack響應。當leader收到超過半數的follower的ack響應之後,就會廣播一個commit訊息給所有follower以通知其進行事務提交,同時leader自身也完成事務的提交。每個follower在接收到commit之後,也會完成對事務的提交。
崩潰恢復
- 當整個服務框架啟動過程中或Leader伺服器出現網路中斷、崩潰退出與重啟等異常情況無法與半數以上的follower聯絡時,ZAB協議就會進入恢復模式。
崩潰恢復下的兩種情況和所要保證的特性
-
ZAB協議需要確保那些已經在Leader伺服器上提交的事務最終被所有伺服器都提交。
- 如果leader在崩潰前發出了proposal1,proposal2,commit1(proposal1的commit),proposal3,commit2(說明leader自己已經commit了proposal2),那麼ZAB需要確保恢復後proposal2在所有伺服器上都被提交成功,否則會出現不一致。
-
ZAB協議需要確保丟棄那些只在Leader伺服器上被提出的事務
- 如果leader伺服器A在崩潰前發出了proposal1,proposal2,commit1(proposal1的commit),proposal3,commit2(說明leader自己已經commit了proposal2),那麼ZAB需要確保恢復後,A重新加入叢集(大概率不是leader了)後,要捨棄proposal3這個事務。
leader選舉演算法
在崩潰恢復過程中需要處理的特殊情況,就決定了ZAB協議必須設計這樣的
- 能夠確保提交已經被Leader提交的事務的Proposal,同時丟棄已經被跳過的事務Proposal。
- 如果讓Leader選舉演算法能夠保證新選舉出來的Leader伺服器擁有叢集所有機器中最高編號(ZXID最大)的事務Proposal,那麼就可以保證這個新選舉出來的Leader一定具有所有已經提交的更改。
- 更為重要的是如果讓具有最高編號事務的Proposal機器成為Leader,就可以省去Leader伺服器查詢Proposal的提交和丟棄工作這一步驟了。
資料同步
- 完成Leader選舉後,在正式開始工作前,Leader伺服器首先會確認日誌中的所有Proposal是否都已經被叢集中的過半機器提交了,即是否完成了資料同步。
- 基於上文講到的兩種情況,資料同步會有不同的處理:
- 同步事務的提交:
- leader為每一個follower都準備一個佇列,並將那些沒有被各follower同步的事務以proposal訊息的形式逐個傳送給follower,並在每個proposal訊息後面緊跟一個commit訊息表示該事務已經被leader提交。等到某個follower同步了所有之前尚未同步的事務並將其成功應用到本地資料庫,leader會將該follower加入到可用follower列表中。
-
處理丟棄的事務
-
下面分析ZAB協議如何處理需要丟棄的事務Proposal的,ZXID是一個64位的數字,其中低32位可以看做是一個簡單的單調遞增的計數器,針對客戶端的每一個事務請求,Leader伺服器在產生一個新的事務Proposal時,都會對該計數器進行加1操作;而高32位則代表了Leader週期epoch的編號,每當選舉產生一個新的Leader時,就會從這個Leader上取出其本地日誌中最大事務Proposal的ZXID,並解析出epoch值,然後加1,之後以該編號作為新的epoch,低32位從0來開始生成新的ZXID。
-
ZAB協議通過epoch號來區分Leader週期變化的策略,能夠有效地避免不同的Leader伺服器錯誤地使用不同的ZXID編號提出不一樣的事務Proposal的異常情況。當一個包含了上一個Leader週期中尚未提交過的事務Proposal的伺服器啟動時,其肯定無法成為Leader,因為當前叢集中一定包含了一個Quorum(過半)集合,該集合中的機器一定包含了更高epoch的事務的Proposal,因此這臺機器的事務Proposal並非最高,也就無法成為Leader。
-
當這臺機器以follower身份連上leader之後,leader會根據自己最後被提交的proposal來和這臺機器的proposal作比較,發現需要捨棄的事務後,leader會要求該臺機器進行回滾操作,回滾到某個被半數機器執行的最新的事務版本。
ZAB和paxos的聯絡和區別
聯絡
- 都存在一個類似於Leader程序的角色,由其負責協調多個Follower程序的執行。
- Leader程序都會等待超過半數的Follower做出正確的反饋後,才會將一個提議進行提交。
- 在ZAB協議中,每個Proposal中都包含了一個epoch值,用來代表當前的Leader週期,在Paxos演算法中,同樣存在這樣的一個標識,名字為Ballot。
區別
- Paxos演算法中,新選舉產生的主程序會進行兩個階段的工作,第一階段稱為讀階段,新的主程序和其他程序通訊來收集主程序提出的提議,並將它們提交。第二階段稱為寫階段,當前主程序開始提出自己的提議。
- ZAB協議在Paxos基礎上添加了同步階段,此時,新的Leader會確保存在過半的Follower已經提交了之前的Leader週期中的所有事務Proposal。
- ZAB協議主要用於構建一個高可用的分散式資料主備系統,而Paxos演算法則用於構建一個分散式的一致性狀