zookeeper、Paxos算法、ZAB協議、CAP理論介紹

分佈式協調服務器Zokeeper

  • Zookeeper概述

1.1 Zookeeper簡介

Zookeeper是一個開源的分佈式應用程序協調服務器,其爲分佈式系統提供一致性服務。其一致性是通過基於Paxos算法的ZAB協議完成的。其主要功能包括:配置維護、域名服務、分佈式同步、集羣管理等。

1.1.1功能簡介

(1)配置維護

分佈式系統中,很多服務都是部署在集羣中的,即很多臺服務器中部署着完全相同的應用,起着完全相同的作用。當然,集羣中的這些服務器的配置文件是完全相同的。

若集羣中服務器的配置文件需要進行修改,那麼我們就需要逐臺修改這些服務器中的配置文件。如果我們集羣服務器比較少,那麼這些修改還不是太麻煩,但如果集羣服務器特別多,比如某些大型互聯網公司的Hadoop集羣有數千臺服務器,那麼純手工的更改這些配置文件幾乎就是一件不可能完成的任務。即使使用大量人力進行修改,但是過多人員參與,出錯的概率大大提升,對於集羣的形成的危險是很大的。

這時候Zookeeper就可以派上用場了。其對於配置文件的維護採用的是發佈/訂閱模型。發佈者將修改好的集羣的配置文件發佈到Zookeeper服務器的文件系統中,那麼訂閱者馬上就可以接收到通知,並主動的去同步Zookeeper裏的配置文件。Zookeeper具有同步操作的原子性,確保每個集羣服務器的配置文件都能被正確的更新。

(2)域名服務

在分佈式應用中,一個項目包含多個工程,有些工程是專門爲其他工程提供服務的。一個項目中可能會存在多種提供不同服務的功能,而一種服務又可能存在多個提供者(服務器)。所以,用於消費這些服務的客戶端工程若要消費這些服務器,就變得異常的複雜了。

此時,Zookeeper就可以上場了。爲每個服務起一個名稱,將這些服務的名稱與提供這些服務的主機地址註冊到Zookeeper中,形成一個服務映射表。服務消費者只需要通過服務名稱即可享受到服務,而無需瞭解服務具體的提供者是誰。服務的減少、添加、變更,只需修改Zookeeper中的服務映射表即可。

阿里的Dubbo就是使用Zookeeper作爲服務域名服務器。

(3)分佈式同步

在分佈式系統中,很多運算(對請求的處理)過程是由分佈式集羣中的若干服務共同計算完成的,並且他們之間的運算還具有邏輯上的先後順序。如何保證這些服務器運行期間的同步性呢?

使用Zookeeper可以協調這些服務器間運算的過程。讓這些服務器都同時監聽Zookeeper上的的同一個znode(Zookeeper文件系統中的一個數據存儲節點),一旦其中一個服務器update了znode,那麼另一個相應服務器能夠收到通知,並作出相應處理。

(4)集羣管理

集羣管理中最麻煩的就是節點故障管理。Zookeeper可以讓集羣選出一個健康的節點作爲Master,Master隨時監控着當前集羣中的每個節點的健康狀況,一旦某個節點發生故障,Master會把這個情況立即通知給集羣中的其他節點,使其他節點對於任務的分配作出相應的調整。Zookeeper不僅可以發現故障,也會對故障進行甄別,如果該故障可以修復,Zookeeper可以自動修復,若不能修復則會告訴系統管理員錯誤的原因讓管理員迅速定位問題。

但這裏也有個問題:Master故障了,那怎麼辦?Zookeeper內部有一個選舉算法,當Master故障出現時,Zookeeper能馬上選出新的Master對集羣進行管理。

1.1.2 一致性要求

什麼是zk的一致性呢?其需要滿足以下幾點要求:

(1)順序一致性

同一個客戶端發起的n多個事務請求(寫請求),最終將會嚴格按照其發起順序被應用到Zookeeper中。

(2)原子性

所有事務請求的結果在集羣中所有機器上的應用情況是一致的。也就是說要麼整個集羣所有主機都成功應用了某一個事務,要麼都沒有應用,不會出現集羣中部分主機應用了該事務,而另外一部分沒有應用到的情況。

(3)單一視圖

無論客戶端連接的是哪個Zookeeper服務器,其看到的服務端數據模型都是一致的。

(4)可靠性

一旦服務端成功的應用了一個事務(寫操作),並完成了對客戶端的響應,那麼該事務所引起的服務端狀態變更將會一直被保留下來,除非有另一個事務又對其進行了變更。

(5)實時性

通常人們看到實時性的第一反應是,一旦一個事務被成功應用,那麼客戶端能夠立即從服務端上讀取到這個事務變更後的最新數據狀態。這裏需要注意的是,Zookeeper僅僅保證在一定的時間段內,客戶端最終一定能夠從服務器上讀取到最新的數據狀態。

1.2 Zookeeper中的重要概念

1.1.1 Session

Session是指客戶端會話。Zookeeper對外的服務端口默認是2181,客戶端啓動時,首先會與zk服務器建立一個TCP長連接,從第一次連接建立開始,客戶端會話的生命週期也開始了。通過這個長連接,客戶端能夠通過心跳檢測保持與服務器的有效會話,也能夠向Zookeeper服務器發送請求並接受響應,同時還能通過該連接接收來自服務器的Watcher事件通知。

Session的SessionTimeout值用來設置一個客戶端會話的超時時間。當由於服務器壓力太大、網絡故障或是客戶端主動斷開鏈接等各種原因導致客戶端連接斷開時,只要在SessionTimeout規定的時間內客戶端能夠重新連接上集羣中任意一臺服務器,那麼之前創建的會話仍然有效。

1.2.2 znode

Zookeeper的文件系統採用樹形層次化的目錄結構,與unix文件系統非常相似。每個目錄在zookeeper中叫做一個znode,每個znode擁有一個唯一的路徑標識,即名稱。Znode可以包含數據和子node(臨時節點不能有子node)。Znode中的數據可以有多個版本,所以查詢某路徑下的數據需帶上版本號。客戶端應用可以在znode上設置監視器(watcher)。

1.2.3 watcher機制

Zk通過watcher機制實現了發佈/訂閱模式。Zk提供了分佈式數據的發佈/訂閱功能,一個發佈者能夠讓多個訂閱者同時監聽某一主題對象,當這個主題對象狀態發生變化時,會通知所有訂閱者,使他們能夠做出相應的處理。Zk引入了watcher機制來實現這種分佈式的通知功能。Zk允許客戶端(訂閱者)向服務端(發佈者)註冊一個watcher監聽,當服務端的一些指定事件觸發這個watcher,那麼就會向指定客戶端發送一個事件通知。而這個事件通知則是通過tcp長連接的session完成的。

1.2.4 ACL

ACL全稱爲Access Control List(訪問控制列表),用於控制資源的訪問權限,是zk數據安全的保障。Zk利用ACL策略控制znode節點的訪問權限,如節點數據讀寫、節點創建、節點刪除、讀取子節點列表、設置節點權限等。

在傳統的文件系統中,ACL分爲兩個維度:組和權限。一個組可以包含多種權限,一個文件或目錄擁有了某個組的權限即擁有了組裏的所有權限。文件或子目錄默認會繼承其父目錄的ACL。

而在zookeeper中,znode的ACL是沒有繼承關係的,每個znode的權限都是獨立控制的,只有客戶端滿足znode設置的權限要求時,才能完成相應的操作。Zookeeper的ACL分爲三個維度:授權策略scheme、用戶id、用戶權限permission。

1.3 Paxos算法

1.3.1 算法簡介

Paxos算法是萊斯利·蘭伯特1990年提出的一種基於消息傳遞的、具有高容錯性的一致性算法。Google chubby(分佈式鎖服務)的作者說過,世上只有一種一致性算法,那就是Paxos

,所有其他一致性算法都是Paxos的不完整版。Paxos算法是一種公認的晦澀難懂的算法,並且工程實現上也具有很大的難度。較有名的Paxos工程實現有google chubby,ZAB,微信的PhxPaxos等。

Paxos算法是用於解決什麼問題的呢?Paxos算法要解決的問題是,在分佈式系統中如何就某個決議達成一致。

1.3.2 Paxos與拜占庭將軍問題

拜占庭將軍問題,是由Paxos算法作者提出的點對點通信中的基本問題。該問題要說明的含義是,在存在消息丟失的不可靠信道上試圖通過消息傳遞的方式達到一致性是不可能的。

Paxos算法的前提是不存在拜占庭將軍問題,即信道是安全的、可靠的,集羣節點間傳遞消息是不會被篡改的。在實際工程實踐中,大多數系統都是部署在一個局域網內,因此消息被篡改的情況很少;另一方面,由於硬件和網絡原因而造成的消息不完整問題,現在已經不再是問題,只需要一套簡單的校驗算法即可。因此,在實際工程中各個服務器間消息傳遞過程可以認爲不存在拜占庭將軍問題。

一般情況下,分佈式系統中各個節點間採用兩種通訊模型:共享內容(shared memory)、消息傳遞(messages passing)。而Paxos是基於消息傳遞通訊模型的。

1.3.3 算法描述

(1)三種角色

在Paxos算法中有三種角色,分別具有三種不同的行爲。但很多時候,一個進程可能同時充當多個角色。

  1. Proposer:提案(Proposal)的提議者。
  2. Acceptor:提案的表決者,即是否accept該提案。只有半數(一般集羣數量是奇數)以上的Acceptor接受了某提案,那麼該提案者被認定爲選定。
  3. Learners:提案的學習者。當提案被選定後,其會同步並執行提案。

一個提案的表決者(Acceptor)會存在多個,但在一個集羣中,提議者(Proposer)也是可能存在多個的,不同的提議者(Proposer)會提出不同的提案。而一致性算法則可以保證如下幾點:

  1. 沒有提案被提出則不會有提案被選定。
  2. 每個提議者在提出提案時都會爲該提案指定一個具有全局唯一性的、遞增的提案編號N,即在整個集羣中是唯一的。
  3. 每個表決者在accept某提案後,將會把該提案的編號N記錄在本地,這樣每個表決者中保存的已經被accept的提案中會存在一個編號最大的提案,其編號假設爲maxN。每個表決者僅會accept變好大於自己本地maxN的提案。
  4. 在衆多提案中最終只能有一個提案被選定。
  5. 一旦一個提案被選定,則其他服務器會主動同步(Learn)該提案到本地。

(2)算法過程描述

Paxos算法的執行過程劃分爲兩個階段:準備階段prepare與接受階段accept。

prepare階段

  1. 提議者(Proposer)準備提交一個編號爲N的提議,於是其首先向所有表決者(Acceptor)發送prepare(N)請求,用於試探集羣是否支持該編號的提議。
  2. 每個表決者(Acceptor)中都保存着自己曾經accept過的提議中的最大編號maxN。當一個表決者接收到其他主機發送來的prepare(N)請求時,其會比較N和MaxN的值。若N小於maxN,則說明該提議已經過時,當前表決者採取不迴應或者回應Error的方式來拒絕該prepare(N)請求;若N大於等於maxN,則說明該提議是可以接受的,當前表決者會將其曾經已經accept的編號最大的提案Proposal(myid,maxN,value)反饋給提議者,以向提議者展示自己支持的提案意願。其中第一個參數myid表示決議者Acceptor的標識id,第二個參數表示其曾接受的提案的最大編號maxN,第三個參數表示該提案的真正內容value。當然,若當前表決者還未曾accept過任何提議,則會將Proposal(myid,null,null)反饋給提議者。

accept階段

  1. 當提議者(Proposer)發出prepare(N)後,若收到了超過半數的表決者(Accepter)的反饋,那麼該提議者就會將真正的提案Proposal(N,value)發送給所有的表決者。
  2. 當表決者(Acceptor)接收到提議者發送的Proposal(N,value)提案後,會再次拿出自己曾經accept過的提議中最大編號maxN,及曾經反饋過的prepare(N)的最大編號,讓N與它們進行比較,若N大於等於這兩個編號,則當前表決者accept該提案,並反饋給提議者。若N小於這兩個編號,則表決者採取不迴應或迴應Error的方式來拒絕該提議。
  3. 若提議者沒有接收到超過半數的表決者的accept反饋,則重新進入prepare階段,遞增提案號,重新提出prepare請求。若提議者接收到的反饋數量超過了半數,則其他的未向提議者發送accept反饋的表決者將成爲Leaner,主動同步提議者的提案。

(3)算法過程舉例

假設有三臺主機,他們要從中選出一個Leader。這三臺主機在不同的時間分別充當提案的提議者Proposer、表決者Acceptor及學習者Learnor三種不同的角色。

這裏首先介紹一下該舉例的前提:每個提議者(Proposer)都想提議自己要當Leader,假設三個提議者Proposer-1、Proposer-2、Proposer-3提議的Proposer-1初始編號依次爲20、10、30.每個提議者都要將提案發送給所有的表決者(Acceptor),爲了便於理解,假設都只有兩個(超過半數)表決者收到消息:Accepter-2與Acceptor-3收到Proposer-2的消息;Accepter-1與Acceptor-2收到了Proposer-1的消息;Accepter-2與Acceptor-3收到了Proposer-3的消息。

A、prepare階段

假設Proposer-1發送的prepare(20)消息先達到Acceptor-1和Acceptor-2。因爲他們之前都沒有接受過prepare請求,所以他們都是直接接收該請求,並將Proposal(server1-id,null,null)與Proposal(server2-id,null,null)反饋給提議者Proposer-1,同時Acceptor-1和Acceptor-2記錄下目前收到的最大提議編號maxN爲20,即其以後不會再接受編號小於20的請求。Proposer-1收到了超過半數的反饋。

緊接着Proposesr-2的prepare(10)消息到達Acceptor-2和Acceptor-3.由於Acceptor-3還未曾接受過其他請求,所以其直接接受Proposer-2的prepare(10)的請求,並返回Proposer-2 Proposal(server3-id,null,null),Acceptor-3會記錄下其目前收到的最大提議編號maxN爲10,即其以後不會再接受編號小於10的請求。另外對於Acceptor-2來說,由於其已經接受Proposer-1的提案20,則會迴應error或者不迴應的方式告訴Proposer-2不接受其提案,此時Proposeser-2接收到的反饋數量是1,沒有超過半數,則會遞增自己的編號,這裏我們默認遞增10,然後會繼續重複發送prepare()

B、accept階段

a、Proposer的提交

  1. Proposer-1收到過半反饋,且這些反饋均未標識自己支持的提案意願,即反饋的value爲null,所以Proposer-1就直接向Acceptor-1和Acceptor-2提交了Proposal(server1-id,20,server1)的提案。
  2. Proposer-2收到了過半反饋,且這些反饋均未表示自己支持的提案意願,即反饋的value爲null,所以Proposer-2就直接向Acceptor-2和Acceptor-3提交了Proposal(server2-id,40,server2)的提案,即遞增自己的編號。
  3. Proposer-3收到過半反饋,且這些反饋均未標識自己支持的提案意願,即反饋的value爲null,所以Proposer-3就直接向Acceptor-2和Acceptor-3提交了Proposal(server3-id,30,server3)的提案。

b、Acceptor的表決

  1. Acceptor-1和Acceptor-2接收到Proposer-1的提案Proposal(server1-id,20,server1)請求,由於Acceptor-1不能接收編號小於20的提案,但可以接收等於20的提案,所以其通過了該提案,但由於Acceptor-2不能接收編號小於40的提案,所以其拒絕了該提案。
  2. Acceptor-2和Acceptor-3接收到Proposer-2的提案Proposal(server2-id,40,server2),它們均通過了該提案。
  3. Acceptor-2和Acceptor-3接收到Proposer-3的提案Proposal(server3-id,30,server3),它們均拒絕了該提案。

由於Acceptor-2和Acceptor-3已經通過了提案Proposal(server2-id,40,server2),並達成了超過半數的一致性,server2馬上成爲了Leader,選舉狀態結束。Server2會發布廣播給所有Leaner,通知它們來同步數據。同步完成後,集羣進入正常服務狀態。

1.3.4 Paxos算法優化

前面所述的Paxos算法在實際工程應用過程中,根據不同的實際需求存在諸多不便之處,所以也就出現了很多對於基本Paxos算法的優化算法,例如,Multi Paxos、Fast Paxos、EPaxos。而Zookeeper的Leader選舉算法FastLeaderElection則是Fast Paxos算法的工程應用。

1.4 ZAB協議

1.4.1 ZAB協議簡介

ZAB,zookeeper Atomic Broadcast,zk原子消息廣播協議,是專爲zookeeper設計的一種支持崩潰回覆的原子廣播協議,是一種Paxos協議的優化算法,在Zookeeper中,主要依賴ZAB協議來實現分佈式數據一致性。

Zookeeper使用一個但一進程來接受並處理客戶端的所有事務請求,即寫請求。當服務器數據的狀態發生變更後,集羣採用ZAB原子廣播協議,以事務提案Proposal的形式廣播到所有的副本進程上。ZAB協議能夠保證一個全局的變更序列,即可以爲每一個事務分配一個全局的遞增編號xid。

當Zookeeper客戶端連接到Zookeeper集羣的一個節點後,若客戶端提交的是讀請求,那麼當前節點就直接根據自己保存的數據對其進行相應;如果是寫請求且當前節點不是Leader,那麼節點就會將該寫請求轉發給Leader,Leader會以提案的方式廣播該寫操作,只要有超過半數節點統一該寫操作,則該寫操作請求就會被提交。然後Leader會再次廣播給所有訂閱者,即Leaner,通知它們同步數據。

1.4.2 三類角色

爲了避免Zookeeper的單點問題,zk也是以集羣的形式出現的。Zk集羣中的角色主要有一下三類:

  1. Leader:zk集羣寫請求的唯一處理者,並負責進行投票的發起和決議,更新系統狀態。Leader是很民主的,並不是說其在接收到寫請求後馬上就修改其中保存的數據,而是首先根據寫請求提出一個提議,在大多數zkServer均同意時纔會做出修改。
  2. Follower:接收客戶端請求,處理讀請求,並向客戶端返回結果;將寫請求轉給Leader;在選主(Leader)過程中參與投票。
  3. Observer:可以理解爲無選舉投票權與寫操作投票權的Follower,其不屬於法定人數範圍,主要是爲了協助Follower處理更多的讀請求。如果Zookeeper集羣的讀請求負載很高時,勢必要增加處理讀請求的服務器數量。若增加的這寫服務器都是以Follwer的身份出現,則會大大降低寫操作的效率。因爲Leader發出的所有寫操作,均需要通過法定人數半數以上同意。過多的Follower會延長Leader與Follower的通信壓力,降低寫操作效率。同樣,過多的Follower會延長Leader的選舉時長,降低整個集羣的可用性。此時,可選擇增加Observer服務器,既提高了處理器讀操作的吞吐量,又沒有增加法定人數。只要法定人數不變,無論是寫操作投票還是選舉投票,其都不會增加通信壓力,都不會影響投票效率。

1.4.3 三種模式.

ZAB協議中對zkServer的狀態描述有三種模式:恢復模式、同步模式和廣播模式。

  1. 恢復模式:在服務重啓過程中,或在Leader崩潰後,就進入了恢復模式,要恢復到zk集羣正常的工作狀態。
  2. 同步模式:在所有的zkServer啓動完畢,或Leader崩潰後又被又被選舉出來時,就進入了同步模式,各個Follower需要馬上將Leader中的數據同步到自己主機中。當大多數zkServer完成了與Leader的狀態同步以後,恢復模式就結束了。所以,同步模式包含在恢復模式過程中。
  3. 廣播模式:當Leader的提議被大多數zkServer同意後,Leader會修改自身數據,然後會將修改後的數據廣播給其他Follower。

1.4.4 zxid

Zxid爲64位長度的Long類型,其中高32位標識紀元epoch,低32位標識事務標識xid,即zxid由兩部分構成:epoch與xid。

每個Leader都會具有不同的epoch值,表示一個時期、時代。每一次新的選舉開啓後都會生成一個新的epoch,新的Leader產生,則會更新所有zkServer的zxid中的epoch。

Xid則爲zk的事務id,每一個寫操作都是一個事務,都會有一個xid。xid爲一個依次遞增的流水號。每一個寫操作都需要由Leader發起一個提案,由所有Follower表決是否同意本次寫操作,而每個提案都具有一個zxid。

1.4.5 消息廣播算法

當集羣中已經有過半的Folower與Leader服務器完成了狀態同步,那麼整個zk集羣就可以進入消息廣播模式了。

如果集羣中的其他節點收到客戶端的事務請求,那麼這些非Leader服務器會首先將這個事務請求轉發給Leader服務器。Leader服務器會爲其生成對應的事務提案Proposal,並將其發送給集羣中其餘其餘所有主機,然後再分別收集他們的選票,在選票過半後進行事務提交。其具體過程如下:

  1. Leader接收到消息請求後,將消息賦予一個全局唯一的64位自增id,即zxid,通過zxid的大小比較即可實現事務的有序性管理。
  2. 爲了保證Leader向Follower發送提案的有序,Leader會爲每個Follower創建一個FIFO隊列,並將提案副本寫入到各個隊列。然後再通過這些隊列將提案發送給各個Follower。
  3. 當Follower接收到提案後,會先將提案的zxid與本地記錄的事務日誌中最大的zxid進行比較。若當前提交的zxid大於最大zxid,則將當前提案記錄到本地事務日誌中,並向Leader返回一個ACK。
  4. 當Leader接收到過半的ACKs後,對於之前回復過Leader的Follower,Leader會向其發送COMMIT消息,批准這些Follower在本地執行該消息;對於之前未回覆過Leader的Follower,Leader會將這些Follower對應的隊列中的提案發送給這些Follower,發送的同時會攜帶COMMIT消息。當Follower收到COMMIT消息後,就會執行該消息。

1.4.6 恢復模式的兩個原則

當集羣正在啓動過程中,或Leader與超過半數的主機斷連後,集羣就進入了恢復模式。對於要恢復的數據狀態需要遵循兩個原則。

(1)已被處理過的消息不能丟

當Leader收到超過半數Follower的ACKs後,就向各個Follower廣播COMMIT消息,批准各個Server執行該寫操作事務。當各個Server在接收到Leader的COMMIT消息後會在本地執行該寫操作,然後會向客戶端響應寫操作成功。但是如果在非全部Follower收到COMMIT消息之前Leader就掛了,這將導致一種後果:部分Server已經執行了該事務,而部分Server尚未收到COMMIT消息,所以其並沒有執行該事務。當新的Leader被選舉出,集羣經過恢復模式後需要保證所有Server上都執行了那些已經被部分Server執行過的事務。

爲了保證已被處理過的消息不能丟的目的,ZAB的恢復模式使用了以下策略:

  1. 選舉擁有proposal最大值(即zxid最大)的節點作爲新的Leader:由於所有提案被COMMIT之前必須有合法數量的Follower ACK,即必須有合法數量的服務器日誌上有該提案,因此,只要有合法數量的節點正常工作,就必然有一個節點保存了所有被COMMIT消息的proposal狀態。
  2. 新的Leader先將自身擁有而並非所有Folower都有的proposal發送給Follower,再將這些proposal的COMMIT命令發送給Follower,以保證所有的Follower都保存並執行了所有的proposal。通過以上策略,能保證已經被處理的消息不會丟。

(2)被丟棄的消息不能再現

當Leader接收到事務請求並生成了proposal,但還未向Follower發送時就掛了。由於其他Follower並沒有收到此proposal,即並不知道該Proposal的存在,因此在經過恢復模式重新選舉產生了新的Leader後,這個事務被跳過。在整個集羣尚未進入正常服務狀態時,之前掛了的Leader主機重新啓動並註冊成爲了Follower。但由於保留了被跳過的proposal,所以其與整個系統的狀態是不一致的,需要將該proposal刪除。

ZAB通過設計巧妙的zxid實現了這一目的。一個zxid是64位,高32位是紀元epoch編號,每一次選舉epoch的值都會增一。低32位是事務標識xid,每產生一個事務,該xid值都會增一。這樣設計的好處是舊的Leader掛了後重啓,它不會被選舉爲新的Leader,因此此時他的zxid肯定小於當前新的epoch。當舊的Leader作爲Follower接入新的Leader後,新的Leader會讓其將所有舊的epoch編號號的、未被COMMIT的proposal清除。

1.4.7 Leader選舉算法

當集羣正在啓動過程中,或Leader與超過半數的主機斷連後,集羣就進入了恢復模式。而恢復模式中最重要的階段就是Leader選舉。

在集羣啓動過程中的Leader選舉過程(算法)與Leader斷連後的Leader選舉過程稍微有一些區別,基本相同。

(1)集羣啓動中的Leader選舉

若進行Leader選舉,則至少需要兩臺主機,這裏以三臺主機組成的集羣爲例。

在集羣初始化階段,當第一臺服務器Server1啓動時,會給自己投票,然後發佈自己的投票結果。投票包含所推舉的服務器的mid和ZXID,使用(mid,ZXID)來表示,此時Server1的投票爲(1,0)。由於其他機器還沒有啓動所以他們收不到反饋信息,Server1的狀態一直屬於Looking,即屬於非服務狀態。

當第二臺服務器Server2啓動時,此時兩臺機器可以相互通信,每臺機器都試圖找到Leader,選舉過程如下:

  1. 每個Server發出一個投票。此時Server1的投票爲(1,0),Server2的投票爲(2,0),然後各自將這個投票發給集羣中的其他機器。
  2. 接受來自各個服務器的投票。集羣的每個服務器收到選票後,首先判斷該選票的有效性,如檢查是否是本輪投票、是否來自LOOKING狀態的服務器。
  3. 處理投票。針對每一個投票,服務器都需要將別人的投票和自己的投票進行PK,PK規則如下:
  1. 優先檢查ZXID。ZXID比較大的服務器優先作爲Leader。
  2. 如果ZXID相同,那麼就比較myid。Myid較大的服務器作爲Leader服務器。

對於Server1而言,它的投票是(1,0),接受Server2的投票爲(2.,0)。其首先會比較兩者的ZXID,均爲0,在比較myid,此時Server2的myid最大,於是Server1更新自己的投票爲(2,0),然後重新投票。對於Server2而言,其無須更新自己的投票,只是再次向集羣中所有主機發出上一次投票信息即可。

  1. 投票統計。每次投票後,每一臺zkServer都會統計投票信息,判斷是否已經有過半機器接收到相同的投票信息。對於Server1,Server2而言,都統計出集羣中已經有兩臺主機接受了(2,0)的投票信息,此時便認爲已經選出了新的Leader,即Server2.
  2. 改變服務器狀態。一旦確定了Leader,每個服務器就會更新自己的狀態,如果是Follower,那麼就變更爲FOLLOWING,如果是Leader,就變更爲LEADING。
  3. 添加主機。在新的Leader選舉出來後Server3啓動,其想發出一輪新的選舉。但由於當前集羣中各個主機的狀態並不是LOOKING,而是各司其職的正常服務,所以其只能是以Follower的身份加入到集羣中。

(2)斷連後的Leader選舉

在Zookeeper運行期間,Leader與非Leader服務器各司其職,即便當有非Leader服務器宕機或新加入時也不會影響Leader。但是若Leader服務器掛了,那麼整個集羣將暫停對外服務,進入新一輪的Leader選舉,其過程和啓動時期的Leader選舉過程基本一致。

假設正在運行的有Server1,Server2,Server3三臺服務器,當前Leader是Server2,若某一時刻Server2掛了,此時便開始新一輪的Leader選舉了。選舉過程如下:

  1. 變更狀態。Leader掛後,餘下的非Observer服務器都會將自己的服務器狀態由FOLLOWING變更爲LOOKING,然後開始進入Leader選舉過程。
  2. 每個Server會發出一個投票,仍然會首先投自己。不過,在運行期間每個服務器上的ZXID可能是不同,此時假定Server1的ZXID爲111,Server3的ZXID爲333;在第一輪投票中,Server1和Server3都會都會投自己,產生投票(1,111),(3,333),然後各自將投票發送給集羣中所有服務器。
  3. 接受來自各個服務器的投票。與啓動時過程相同。集羣的每個服務器收到投票後,首先判斷該投票的有效性,如檢查是否是本輪投票、是否來自LOOING狀態的服務器。
  4. 處理投票。與啓動過程相同。針對每一個投票,服務器都需要將別人的投票和自己的投票進行PK。對於Server1而言,他的投票是(1,111),接收Server3的投票爲(3,333)。其首先會比較兩者的ZXID,Server3投票的zxid爲333大於Server1投票的zxid的111,於是Server1更新自己的投票爲(3,333),然後重新投票。對於Server3而言,其無須更新自己的投票,只是再次向集羣中所有主機發出上一次投票信息即可。
  5. 統計投票。與啓動時過程相同。經過票數統計,最終Server3當選新的Leader。
  6. 改變服務器的狀態。與啓動時過程相同。一旦確定了Leader,每個服務器就會更新自己的狀態。Server1變更爲FOLLOWER,Server3變更爲LEADING。

1.4.8 恢復模式下的數據同步

當完成Leader選舉後,就要進入到恢復模式下的數據同步階段。Leader服務器會爲每一個Follower服務器準備一個隊列,並將那些沒有被各個Follower服務器同步的事務以Proposal的形式逐條發給各個Follower服務器,並在每一個Proposal後緊跟一個commit消息,表示該事務已經被提交,Follower可以直接接收並執行。當Follower服務器將所有尚未同步的事務proposal都從leader服務器同步過來併成功執行後,會向準leader發送ACK信息。Leader服務器在收到該ACK後就會將該follower加入到真正可用的follower列表。

1.5 CAP原則

1.5.1 簡介

CAP原則又稱CAP定理,指的是在一個分佈式系統中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性),三者不可兼得。

  1. 一致性(C):分佈式系統中的所有主機中在同一時刻是否可以保證具有完全相同的數據備份。若具有,則該分佈式系統具有一致性。
  2. 可用性(A):在集羣中部分節點發生故障後,是否會影響對客戶端讀寫請求的響應。注意,若在短時間內會影響,其也不具備這裏所說的可用性。
  3. 分區容錯性(P):分佈式系統中的一臺主機稱爲一個分區。那麼,什麼是分佈式系統的錯誤呢?分佈式系統中各個主機無法保證數據的一致性是一種錯誤;分佈式系統無法及時響應客戶端請求,即不具有可用性也是一種錯誤。對於分佈式系統,必須要具有對這些錯誤的包容性,即容錯性,這是必須的。

1.5.2 三二原則

對於分佈式系統,在CAP原則中分區容錯性P是必須要保證的。但其並不能同時保證一致性與可用性。因爲現在的分佈式系統在滿足一致性的前提下,會犧牲可用性;在滿足了可用性的前提下,會犧牲一致性。所以,CAP原則對於一個分佈式系統來說,只可能滿足兩項,即要麼CP,要麼AP。這就是CAP的三二原則。

1.5.3 ZK與CP

Zk遵循的是CP原則,即保證了一致性,但犧牲了可用性。體現在哪裏呢?

當Leader宕機後,zk集羣會馬上進行新的Leader的選舉。但選舉時長在30-120秒之間,整個選舉期間選舉期間kzk集羣是不接受客戶端的讀寫操作的,即zi集羣是出於癱瘓狀態的。所以,其不滿足可用性。

爲什麼Leader的選舉需要這麼長時間呢?爲了保證zk集羣各個節點中數據的一致性,zk集羣做了兩類數據同步:初始化同步與更新同步。當新的Leader被選舉出後,各個Follower需要將新的Leader的數據同步到自己的緩存中,這是初始化同步;當Leader的數據被客戶端修改後,其會向Follower發出廣播,然後各個Follower會主動同步Leader的更新數據,這是更新同步。無論是初始化同步還是更新同步,zk集羣爲了保證數據的一致性,若發現超過半數的Follower同步超時,則其會再次進行同步,而這個過程中zk集羣是處於不可用狀態的。

由於zk採用了CP原則,所以導致其可用性降低,這是致命的問題。Spring Cloud的Eureka在分佈式系統中所起的作用類似於zk,但其採用了AP原則,其犧牲了一致性,但保證了可用性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章