ZooKeeper的工做原理

 ZooKeeper是一個分佈式的應用程序協調服務php

 

2 ZooKeeper的工做原理

Zookeeper 的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫作Zab(Zookeeper Atomic Broadcast)協議。Zab協議有兩種模式,它們分別是恢復模式(recovery選主) 廣播模式(broadcast同步)。當服務啓動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了leader狀態同步以 後,恢復模式就結束了。狀態同步保證了leaderServer具備相同的系統狀態node

 

 1ZooKeeper數據模型算法

相似於一個標準的文件系統具備層次關係的數據結構設計模式

每一個子目錄項如NameService都被稱做爲znode服務器

ZNode根據其自己的特性,能夠分爲下面兩類網絡

Regular ZNode: 常規型ZNodesession

Ephemeral ZNode: (ɪ'fem(ə)r(ə)l)(臨時的)類型的目錄節點不能有子節點目錄。數據結構

Zookeeper的客戶端和服務器通訊採用長鏈接方式,每一個客戶 端和服務器經過心跳來保持鏈接,這個鏈接狀態稱爲session,若是znode是臨時節點,這個session失效,znode也就刪除了。(3s/一次,200)異步

若是Client由於TimeoutZookeeper Server失去鏈接,client處在CONNECTING狀態,會自動嘗試再去鏈接Server,若是在session有效期內再次成功鏈接到某個Server,則回到CONNECTED狀態。分佈式

 

2ZooKeeper Watch

 

    Zookeeper從設計模式的角度來看,是一個基於觀察者設計模式設計的。簡單來講就是

 Client能夠在某個ZNode上設置一個Watcher,來WatchZNode上的變化。若是該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設置WatcherClient。須要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,若是想繼續Watch的話,須要客戶端從新設置Watcher

 

三、 ZooKeeper特性 

 

   讀、寫(更新)模式

 

  ZooKeeper集羣中,讀能夠從任意一個ZooKeeper Server寫的請求會先ForwarderLeader,而後由Leader來經過ZooKeeper中的原子廣播協議,將請求廣播給全部的FollowerLeader收到一半以上的寫成功的消息後,就認爲該寫成功了,就會將該寫進行持久化,並告訴客戶端寫成功了。

 

   FIFO
對於每個ZooKeeper客戶端而言,全部的操做都是遵循FIFO順序的,這一特性是由下面兩個基本特性來保證的:一是ZooKeeper ClientServer之間的網絡通訊是基於TCPTCP保證了Client/Server之間傳輸包的順序;二是ZooKeeper Server執行客戶端請求也是嚴格按照FIFO順序的。

了保證事務的順序一致性,zookeeper採用了遞增的事務id號(zxid)來標識事務。全部的提議(proposal)都在被提出的時候加上了 zxid。實現中zxid是一個64位的數字,它高32位是 用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個新 的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。

 

 ZooKeeper典型應用場景

  1. 名字服務(NameService) 

每一個ZNode均可以由其路徑惟一標識,路徑自己也比較簡潔直觀,另外ZNode上還能夠存儲少許數據,這些都是實現統一的NameService的基礎。經過簡單的名字,訪問對應的服務器集羣。

 

  1. 配置管理(Configuration Management) 

 

 

 

:分佈式互斥鎖 

在傳統的應用程序中,線程、進程的同步,均可以經過操做系統提供的機制來完成。可是在分佈式系統中,多個進程之間的同步,操做系統層面就無能爲力了。

zookeeper中,並無JAVA裏同樣有Synchronized或者是ReentrantLock機制來實現鎖機制,可是在zookeeper中,實現起來更簡單:咱們能夠講將zk的一個數據節點表明一個鎖,當多個客戶端同時調用create()節點建立節點的時候,zookeeper會保證只會有一個客戶端建立成功,那麼咱們就可讓這個建立成功的客戶端讓其持有鎖,而其它的客戶端則註冊Watcher監聽當持有鎖的客戶端釋放鎖後,監聽的客戶端就會收到Watcher通知,而後再去試圖獲取鎖,這樣反覆便可。

Zookeeper的三種角色:

1.leaderfollower 

 

      ZooKeeper須要在全部的服務(能夠理解爲服務器)中選舉出一個Leader,而後讓這個Leader來負責管理集羣。此時,集羣中的其它服務器則 成爲此LeaderFollower。而且,當Leader故障的時候,須要ZooKeeper可以快速地在Follower中選舉出下一個 Leader。這就是ZooKeeperLeader機制,下面咱們將簡單介紹在ZooKeeper中,Leader選舉(Leader Election)是如何實現的。 

此操做實現的核心思想是:首先建立一個EPHEMERAL目錄節點,例如「/election」。而後。每個ZooKeeper服務器在此目錄 下建立一個SEQUENCE|EPHEMERAL類型的節點,例如「/election/n_」。在SEQUENCE標誌下,ZooKeeper將自動地 爲每個ZooKeeper服務器分配一個比前一個分配的序號要大的序號。此時建立節點的ZooKeeper服務器中擁有最小序號編號的服務器將成爲 Leader

在實際的操做中,還須要保障:當Leader服務器發生故障的時候,系統可以快速地選出下一個ZooKeeper服務器做爲Leader。一個簡 單的解決方案是,讓全部的follower監視leader所對應的節點。當Leader發生故障時,Leader所對應的臨時節點將會自動地被刪除,此 操做將會觸發全部監視Leader的服務器的watch。這樣這些服務器將會收到Leader故障的消息,並進而進行下一次的Leader選舉操做。但 是,這種操做將會致使從衆效應的發生,尤爲當集羣中服務器衆多而且帶寬延遲比較大的時候,此種狀況更爲明顯。

Zookeeper中,爲了不從衆效應的發生,它是這樣來實現的:每個followerfollower集羣中對應的比本身節點序號小一 號的節點(也就是全部序號比本身小的節點中的序號最大的節點)設置一個watch。只有當follower所設置的watch被觸發的時候,它才進行 Leader選舉操做,通常狀況下它將成爲集羣中的下一個Leader。很明顯,此Leader選舉操做的速度是很快的。由於,每一次Leader選舉幾 乎只涉及單個follower的操做

2.Observer

      observer的行爲在大多數狀況下與follower徹底一致, 可是他們不參加選舉和投票, 而僅僅接受(observing)選舉和投票的結果.

Zookeeper集羣,選舉機制

 

 

zookeeper選舉機制

FastLeaderElection算法經過異步的通訊方式來收集其它節點的選票,同時在分析選票時又根據投票者的當前狀態來做不一樣的處理,以加快Leader的選舉進程。    
    每一個在zookeeper服務器啓動先讀取當前保存在磁盤的數據,zookeeper中的每份數據都有一個對應的id值,這個值是依次遞增的;換言之,越新的數據,對應的ID值就越大。 
    在讀取數據完畢以後,每一個zookeeper服務器發送本身選舉的leader,這個協議中包含了如下幾部分的數據: 
1)、所選舉leader的id(就是配置文件中寫好的每一個服務器的id) ,在初始階段,每臺服務器的這個值都是本身服務器的id,也就是它們都選舉本身爲leader。 
2)、服務器最大數據的id,這個值大的服務器,說明存放了更新的數據。 
3)、邏輯時鐘的值,這個值從0開始遞增,每次選舉對應一個值,也就是說:若是在同一次選舉中,那麼這個值應該是一致的,邏輯時鐘值越大,說明這一次選舉leader的進程更新。 
4)、本機在當前選舉過程當中的狀態,有如下幾種:LOOKING,FOLLOWING,OBSERVING,LEADING 

    每臺服務器將本身服務器的以上數據發送到集羣中的其餘服務器以後,一樣的也須要接收來自其餘服務器的數據,它將作如下的處理: 
A、若是所接收數據服務器的狀態仍是在選舉階段(LOOKING 狀態),那麼首先判斷邏輯時鐘值,又分爲如下三種狀況: 
a) 若是發送過來的邏輯時鐘大於目前的邏輯時鐘,那麼說明這是更新的一次選舉,此時須要更新一下本機的邏輯時鐘值,代碼以下: 

if (n.epoch > logicalclock) { logicalclock = n.epoch; recvset.clear(); if(totalOrderPredicate(n.leader, n.zxid,getInitId(), getInitLastLoggedZxid())) updateProposal(n.leader, n.zxid); else updateProposal(getInitId(),getInitLastLoggedZxid()); sendNotifications();


其中的totalOrderPredicate函數就是根據發送過來的封包中的leader id,數據id來與本機保存的相應數據進行判斷的函數(首先看數據id,數據id大者勝出;其次再判斷leader id,leader id大者勝出),返回true則調用updateProposal函數更新數據。 
b) 發送過來數據的邏輯時鐘小於本機的邏輯時鐘 
說明對方在一個相對較早的選舉進程中,這裏只須要將本機的數據廣播出去 
c) 兩邊的邏輯時鐘相同,此時也只是調用totalOrderPredicate函數判斷是否須要更新本機的數據,將最新的選舉結果廣播出去 


B、若是所接收服務器不在選舉狀態,也就是在FOLLOWING或者LEADING狀態 
a) 若是邏輯時鐘相同,將該數據保存到recvset,若是所接收服務器宣稱本身是leader,那麼將判斷是否是有半數以上的服務器選舉它,若是是則設置選舉狀態退出選舉過程 
若是邏輯時鐘不相同,那麼說明在另外一個選舉過程當中已經有了選舉結果,因而將該選舉結果加入到outofelection集合中,再根 據outofelection來判斷是否能夠結束選舉,若是能夠也是保存邏輯時鐘,設置選舉狀態,退出選舉過程 

以一個簡單的例子來講明整個選舉的過程. 
假設有五臺服務器組成的zookeeper集羣,它們的id從1-5,同時它們都是最新啓動的,也就是沒有歷史數據,在存放數據量這一點上,都是同樣的.假設這些服務器依序啓動,來看看會發生什麼 

1) 服務器1啓動,此時只有它一臺服務器啓動了,它發出去的報沒有任何響應,因此它的選舉狀態一直是LOOKING狀態  
2) 服務器2啓動,它與最開始啓動的服務器1進行通訊,互相交換本身的選舉結果,因爲二者都沒有歷史數據,因此id值較大的服務器2勝出,可是因爲沒有達到超 過半數以上的服務器都贊成選舉它(這個例子中的半數以上是3),因此服務器1,2仍是繼續保持LOOKING狀態.  
3) 服務器3啓動,根據前面的理論分析,服務器3成爲服務器1,2,3中的老大,而與上面不一樣的是,此時有三臺服務器選舉了它,因此它成爲了此次選舉的leader.  
4) 服務器4啓動,根據前面的分析,理論上服務器4應該是服務器1,2,3,4中最大的,可是因爲前面已經有半數以上的服務器選舉了服務器3,因此它只能接收當小弟的命了.  5) 服務器5啓動,同4同樣,當小弟

相關文章
相關標籤/搜索