因爲一臺服務器的處理能力是有限的,在大用戶量和高併發的狀況下,一般須要不少臺服務器同時工做對外提供服務。這麼多機器同時工做,怎麼來管理這些服務器呢?好比某臺服務器宕機了,就要確保請求再也不發送到這臺服務器;某個程序的配置修改了,多臺服務器上的配置要做相應修改;或者多個服務必須按照特定的順序執行;若是多個服務是有事務的,若是中間某個服務失敗了,那這多個服務都必須回滾。對於這些狀況最好是有一個專門的管理中心來管理,而ZooKeeper就能夠做爲這個管理中心。node
ZooKeeper是Apache下的一個Java開源項目(最初由Yahoo開發,後捐獻給了Apache)。
ZooKeeper的原始功能很簡單,基於它的層次型的目錄樹的數據結構,並經過對樹上的節點進行有效管理,能夠設計出各類各樣的分佈式集羣管理功能。此外,ZooKeeper自己也是分佈式的。算法
Zookeeper 會維護一個具備層次關係的樹狀的數據結構,它很是相似於一個標準的文件系統,以下圖所示:
數據庫
ZooKeeper樹狀結構中的每個節點稱做——<font color=#D2691E>Znode</font>。每個節點都有一個名稱,且以 /
開頭,其中最頂層的節點是 /
,最終的狀況就是每個節點在樹狀結構中,會有一個相似絕對路徑的惟一標識,如上圖中的 Server1
這個Znode 的標識爲 /NameService/Server1
服務器
雖然ZooKeeper的樹狀結構相似文件系統,可是Znode兼有文件和目錄的特色,一個Znode既能在它下面建立子節點,做爲路徑標識的一部分,同時這個節點同時也能存儲數據,但這個存儲不是設計用來做常規的數據庫存儲,而主要存放分佈式應用的配置信息、狀態信息等,這些數據的共同特性就是它們都是很小的數據,一般以KB爲單位。網絡
ZooKeeper數據模型中的每一個Znode都維護着一個 <font color=#D2691E>stat</font> 結構。
一個stat僅提供一個Znode的元數據。它由版本號,操做控制列表(ACL),時間戳和數據長度組成。session
版本號 - 每一個Znode都有版本號,這意味着每當與Znode相關聯的數據發生變化時,其對應的版本號也會增長。當多個ZooKeeper客戶端嘗試在同一Znode上執行操做時,版本號的使用就很重要。數據結構
操做控制列表(ACL) - ACL基本上是訪問Znode的認證機制。它管理全部Znode讀取和寫入操做。每個節點都擁有本身的ACL,這個列表規定了用戶的權限,即限定了特定用戶對目標節點能夠執行的操做。權限的種類有:併發
時間戳 - 導致ZooKeeper節點狀態改變的每個操做都將使節點接收到一個Zxid格式的時間戳,而且這個時間戳全局有序。也就是說,每一個對節點的改變都將產生一個惟一的Zxid。若是Zxid1的值小於Zxid2的值,那麼Zxid1所對應的事件發生在Zxid2所對應的事件以前。實際上,ZooKeeper的每一個節點維護者三個Zxid值,爲別爲:cZxid、mZxid、pZxid。app
每一個Znode由3部分組成:異步
ZooKeeper中的節點有兩種,分別爲臨時(ephemeral)節點和永久(persistent)節點。節點的類型在建立時即被肯定,而且不能改變。
<font color=#D2691E>順序節點</font>能夠是持久的或臨時的。當一個新的Znode被建立爲一個順序節點時,ZooKeeper經過將10位的序列號附加到原始名稱來設置Znode的路徑。例如,若是將具備路徑/myapp
的Znode建立爲順序節點,則ZooKeeper會將路徑更改成/myapp0000000001
,並將下一個序列號設置爲0000000002
,這個序列號由父節點維護。若是兩個順序節點是同時建立的,那麼ZooKeeper不會對每一個Znode使用相同的數字。順序節點在鎖定和同步中起重要做用,順序號能夠被用於爲全部的事件進行全局排序,這樣客戶端能夠經過順序號推斷事件的順序。
綜合上面兩節內容,ZooKeeper有四種形式的節點:
一個節點自身擁有表示其狀態的許多重要屬性,以下圖所示。
Zookeeper 的客戶端和服務器通訊採用長鏈接方式,每一個客戶端和服務器經過心跳來保持鏈接,這個鏈接狀態稱爲 Session。
會話對於ZooKeeper的操做很是重要。會話中的請求按FIFO順序執行。一旦客戶端鏈接到服務器,將創建會話並向客戶端分配會話ID 。
客戶端以特定的時間間隔發送心跳以保持會話有效。若是ZooKeeper集羣在超過指定的時間都沒有從客戶端接收到心跳,則會話會被認爲結束(會話超時)。會話超時一般以毫秒爲單位。
Client和Zookeeper集羣創建鏈接,整個session狀態變化如圖所示:
若是Client由於Timeout和Zookeeper Server失去鏈接,client處在CONNECTING狀態,會自動嘗試再去鏈接Server,若是在session有效期內再次成功鏈接到某個Server,則回到CONNECTED狀態。
注意:若是由於網絡狀態很差,client和Server失去聯繫,client會停留在當前狀態,會嘗試主動再次鏈接Zookeeper Server。client不能宣稱本身的session expired,session expired是由Zookeeper Server來決定的,client能夠選擇本身主動關閉session。
Zookeeper watch是一種監聽通知機制。客戶端註冊監聽它關心的目錄節點,當目錄節點發生變化(數據改變、被刪除、子目錄節點增長刪除)時,zookeeper會通知客戶端。,監視事件能夠理解爲一次性的觸發器。官方定義以下:
a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。
Watch的關鍵點:
當設置監視的數據發生改變時,該監視事件會被髮送到客戶端,例如,若是客戶端調用了getData("/znode1", true) 而且稍後 /znode1 節點上的數據發生了改變或者被刪除了,客戶端將會獲取到 /znode1 發生變化的監視事件,而若是 /znode1 再一次發生了變化,除非客戶端再次對/znode1 設置監視,不然客戶端不會收到事件通知。
Zookeeper客戶端和服務端是經過 socket 進行通訊的,因爲網絡存在故障,因此監視事件頗有可能不會成功地到達客戶端,監視事件是異步發送至監視者的,Zookeeper 自己提供了順序保證(ordering guarantee):即客戶端只有首先看到了監視事件後,纔會感知到它所設置監視的znode發生了變化(a client will never see a change for which it has set a watch until it first sees the watch event)。網絡延遲或者其餘因素可能致使不一樣的客戶端在不一樣的時刻感知某一監視事件,可是不一樣的客戶端所看到的一切具備一致的順序(有序一致性)。
ZooKeeper能夠爲全部的讀操做設置watch,這些讀操做包括:exists()、getChildren()及getData()。
ZooKeeper所管理的watch能夠分爲兩類:
咱們能夠經過操做返回的數據來設置不一樣的watch:
所以
watch註冊與處觸發
Zookeeper 中的監視是輕量級的,所以容易設置、維護和分發。當客戶端與 Zookeeper 服務器失去聯繫時,客戶端並不會收到監視事件的通知,只有當客戶端從新鏈接後,若在必要的狀況下,之前註冊的監視會從新被註冊並觸發,對於開發人員來講這一般是透明的。只有一種狀況會致使監視事件的丟失,即:經過exists()設置了某個znode節點的監視,可是若是某個客戶端在此znode節點被建立和刪除的時間間隔內與zookeeper服務器失去了聯繫,該客戶端即便稍後從新鏈接 zookeeper服務器後也得不到事件通知。
咱們使用ZooKeeper,簡單地理解就是使用ZooKeeper的<font color=#D2691E>文件系統+通知機制</font>。
Zookeeper服務自身組成一個集羣(2n+1個服務容許n個失效)。在Zookeeper集羣中,主要分爲三者角色,而每個節點同時只能扮演一種角色,這三種角色分別是:
<font color=#D2691E>Leader</font>:
<font color=#D2691E>Follower</font>:
<font color=#D2691E>Observer</font>:
Propsal投票:每個事務都須要集羣中超過半數的機器投票承認才能被真正地應用到ZK的內存數據庫中。
下圖描述了 ZooKeeper集羣「客戶端-服務端」的結構
Zookeeper提供的一致性是弱一致性,數據的同步有以下規則:ZooKeeper確保對znode樹的每個修改都會被同步到集羣中超過半數的機器上,那麼就認爲更新成功。因此就有可能有節點的數據不是最新的而被客戶端訪問到。而且會有一個時間點,數據在集羣中是不一致的.也就是Zookeeper只保證最終一致性,可是實時的一致性能夠由客戶端調用本身來保證,經過調用sync()方法
sync()
接口。可靠性:一旦一個更新操做被應用,那麼在客戶端再次更新它以前,它的值將不會改變。這個保證將會產生下面兩種結果:
有了這些一致性保證, ZooKeeper 更高級功能的設計與實現將會變得很是容易,例如: leader 選舉、隊列以及可撤銷鎖等機制的實現。
用分佈式系統的CAP原則來分析ZooKeeper.
sync()
P: 有2點須要分析的.
在ZooKeeper的集羣中,各個節點共有下面3種角色和4種狀態:
4種狀態的解釋:
ZooKeeper的核心是原子廣播,這個機制保證了各個server之間的同步。實現這個機制的協議叫作Zab協議(ZooKeeper Atomic Broadcast protocol)。Zab協議有兩種模式,它們分別是恢復模式(Recovery選主)和廣播模式(Broadcast同步)。
當服務啓動或者在leader崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數server完成了和leader的狀態同步之後,恢復模式就結束了。狀態同步保證了leader和Server具備相同的系統狀態。
爲了保證事務的順序一致性,ZooKeeper採用了遞增的事務id號(zxid)來標識事務。全部的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個新的epoch(每leader選舉一次+1),標識當前屬於那個leader的統治時期。低32位爲事務操做次數(每增長一次事務+1)
當leader崩潰或者集羣啓動,這時候zk進入恢復模式,恢復模式須要從新選舉出一個新的leader,讓全部的Server都恢復到一個正確的狀態。Zk的選舉算法有兩種:一種是基於basic paxos實現的,另一種是基於fast paxos算法實現的。系統默認的選舉算法爲fast paxos。先介紹basic paxos流程:
經過流程分析咱們能夠得出:要使Leader得到多數Server的支持,則Server總數必須是奇數2n+1,且存活的Server的數目不得少於n+1.
每一個Server啓動後都會重複以上流程。在恢復模式下,若是是剛從崩潰狀態恢復的或者剛啓動的server還會從磁盤快照中恢復數據和會話信息,zk會記錄事務日誌並按期進行快照,方便在恢復時進行狀態恢復。選主的具體流程圖以下所示:
fast paxos流程是在選舉過程當中,某Server首先向全部Server提議本身要成爲leader,當其它Server收到提議之後,解決epoch和zxid的衝突,並接受對方的提議,而後向對方發送接受提議完成的消息,重複這個流程,最後必定能選舉出Leader。其流程以下所示: