ZooKeeper的詳細介紹以及底層原理

前言

提到ZooKeeper,相信你們都不會陌生。Dubbo,Kafka,Hadoop等等項目裏都能看到它的影子。可是你真的瞭解 ZooKeeper 嗎?若是面試官讓你給他講講 ZooKeeper 是個什麼東西,你能回答到什麼地步呢?html

我會用兩個篇幅介紹ZooKeeper ,第一篇是概念性的認識,這篇你會獲得 ZooKeeper 是什麼,ZooKeeper 設計的目標,ZooKeeper 能作什麼和ZooKeeper 基本的概念。第二篇我會從實戰出發,安裝ZooKeeper,寫一些ZooKeeper 具體應用場景的代碼實現。node

1、ZooKeeper是什麼

ZooKeeper 是一個分佈式的,開放源碼的分佈式應用程序協調服務,是Google的Chubby一個開源的實現,是Hadoop和Hbase的重要組件。它是一個爲分佈式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分佈式同步、組服務等。git

2、ZooKeeper 的由來

下面這段內容摘自《從Paxos到Zookeeper 》 ,本文中不少的名詞介紹也來自本書。算法

Zookeeper 最先起源於雅虎研究院的一個研究小組。在當時,研究人員發現,在雅虎內部不少大型系統基本都須要依賴一個相似的系統來進行分佈式協調,可是這些系統每每都存在分佈式單點問題。因此,雅虎的開發人員就試圖開發一個通用的無單點問題的分佈式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。
關於「ZooKeeper」這個項目的名字,其實也有一段趣聞。在立項初期,考慮到以前內部不少項目都是使用動物的名字來命名的(例如著名的Pig項目),雅虎的工程師但願給這個項目也取一個動物的名字。時任研究院的首席科學家 RaghuRamakrishnan 開玩笑地說:「在這樣下去,咱們這兒就變成動物園了!」此話一出,你們紛紛表示就叫動物園管理員吧一一一由於各個以動物命名的分佈式組件放在一塊兒,雅虎的整個分佈式系統看上去就像一個大型的動物園了,而 Zookeeper 正好要用來進行分佈式環境的協調一一因而,Zookeeper 的名字也就由此誕生了。apache

3、ZooKeeper的特性

  • 順序一致性,從同一個客戶端發起的事務請求,最終將會嚴格地按照其發起順序被應用到Zookeeper中去。安全

  • 原子性,全部事務請求的處理結果在整個集羣中全部機器上的應用狀況是一致的,即整個集羣要麼都成功應用了某個事務,要麼都沒有應用。服務器

  • 單一視圖,不管客戶端鏈接的是哪一個 Zookeeper 服務器,其看到的服務端數據模型都是一致的。網絡

  • 可靠性,一旦服務端成功地應用了一個事務,並完成對客戶端的響應,那麼該事務所引發的服務端狀態變動將會一直被保留,除非有另外一個事務對其進行了變動。

  • 實時性,Zookeeper 保證在必定的時間段內,客戶端最終必定可以從服務端上讀取到最新的數據狀態。

4、ZooKeeper的設計目標

  • 簡單的數據結構
    Zookeeper 使得分佈式程序可以經過一個共享的樹形結構的名字空間來進行相互協調,即Zookeeper 服務器內存中的數據模型由一系列被稱爲ZNode的數據節點組成,Zookeeper 將全量的數據存儲在內存中,以此來提升服務器吞吐、減小延遲的目的。

  • 能夠構建集羣
    Zookeeper 集羣一般由一組機器構成,組成 Zookeeper 集羣的而每臺機器都會在內存中維護當前服務器狀態,而且每臺機器之間都相互通訊。

  • 順序訪問
    對於來自客戶端的每一個更新請求,Zookeeper 都會分配一個全局惟一的遞增編號,這個編號反映了全部事務操做的前後順序。

  • 高性能
    Zookeeper 和Redis同樣全量數據存儲在內存中,100%讀請求壓測QPS 12-13W。

5、關於 ZooKeeper 的一些重要概念

5.1 Zookeeper 集羣:

Zookeeper 是一個由多個 server 組成的集羣,一個 leader,多個 follower。(這個不一樣於咱們常見的Master/Slave模式)leader 爲客戶端服務器提供讀寫服務,除了leader外其餘的機器只能提供讀服務。
每一個 server 保存一份數據副本全數據一致,分佈式讀 follower,寫由 leader 實施更新請求轉發,由 leader 實施更新請求順序進行,來自同一個 client 的更新請求按其發送順序依次執行數據更新原子性,一次數據更新要麼成功,要麼失敗。全局惟一數據視圖,client 不管鏈接到哪一個 server,數據視圖都是一致的實時性,在必定事件範圍內,client 能讀到最新數據。

5.2 集羣角色

Leader:是整個 Zookeeper 集羣工做機制中的核心 。Leader 做爲整個 ZooKeeper 集羣的主節點,負責響應全部對 ZooKeeper 狀態變動的請求。
主要工做:

  • 事務請求的惟一調度和處理,保障集羣處理事務的順序性。
  • 集羣內各服務器的調度者。

Leader 選舉是 Zookeeper 最重要的技術之一,也是保障分佈式數據一致性的關鍵所在。咱們以三臺機器爲例,在服務器集羣初始化階段,當有一臺服務器Server1啓動時候是沒法完成選舉的,當第二臺機器 Server2 啓動後兩臺機器能互相通訊,每臺機器都試圖找到一個leader,因而便進入了 leader 選舉流程.

  1. 每一個 server 發出一個投票
    投票的最基本元素是(SID-服務器id,ZXID-事物id)
  2. 接受來自各個服務器的投票
  3. 處理投票
    優先檢查 ZXID(數據越新ZXID越大),ZXID比較大的做爲leader,ZXID同樣的狀況下比較SID
  4. 統計投票
    這裏有個過半的概念,大於集羣機器數量的一半,即大於或等於(n/2+1),咱們這裏的由三臺,大於等於2即爲達到「過半」的要求。
    這裏也有引伸到爲何 Zookeeper 集羣推薦是單數。
集羣數量 至少正常運行數量 容許掛掉的數量
2 2的半數爲1,半數以上最少爲2 0
3 3的半數爲1.5,半數以上最少爲2 1
4 4的半數爲2,半數以上最少爲3 1
5 5的半數爲2.5,半數以上最少爲3 2
6 6的半數爲3,半數以上最少爲4 2

經過以上能夠發現,3臺服務器和4臺服務器都最多容許1臺服務器掛掉,5臺服務器和6臺服務器都最多容許2臺服務器掛掉,明顯4臺服務器成本高於3臺服務器成本,6臺服務器成本高於5服務器成本。這是因爲半數以上投票經過決定的。

  1. 改變服務器狀態
    一旦肯定了 leader,服務器就會更改本身的狀態,且一半不會再發生變化,好比新機器加入集羣、非 leader 掛掉一臺。

Follower :是 Zookeeper 集羣狀態的跟隨者。他的邏輯就比較簡單。除了響應本服務器上的讀請求外,follower 還要處理leader 的提議,並在 leader 提交該提議時在本地也進行提交。另外須要注意的是,leader 和 follower 構成ZooKeeper 集羣的法定人數,也就是說,只有他們才參與新 leader的選舉、響應 leader 的提議。

Observer :服務器充當一個觀察者的角色。若是 ZooKeeper 集羣的讀取負載很高,或者客戶端多到跨機房,能夠設置一些 observer 服務器,以提升讀取的吞吐量。Observer 和 Follower 比較類似,只有一些小區別:首先 observer 不屬於法定人數,即不參加選舉也不響應提議,也不參與寫操做的「過半寫成功」策略;其次是 observer 不須要將事務持久化到磁盤,一旦 observer 被重啓,須要從 leader 從新同步整個名字空間。

5.3會話(Session)

Session 指的是 ZooKeeper 服務器與客戶端會話。在 ZooKeeper 中,一個客戶端鏈接是指客戶端和服務器之間的一個 TCP 長鏈接。客戶端啓動的時候,首先會與服務器創建一個 TCP 鏈接,從第一次鏈接創建開始,客戶端會話的生命週期也開始了。經過這個鏈接,客戶端可以經過心跳檢測與服務器保持有效的會話,也可以向Zookeeper 服務器發送請求並接受響應,同時還可以經過該鏈接接收來自服務器的Watch事件通知。 Session 的 sessionTimeout 值用來設置一個客戶端會話的超時時間。當因爲服務器壓力太大、網絡故障或是客戶端主動斷開鏈接等各類緣由致使客戶端鏈接斷開時,只要在sessionTimeout規定的時間內可以從新鏈接上集羣中任意一臺服務器,那麼以前建立的會話仍然有效。在爲客戶端建立會話以前,服務端首先會爲每一個客戶端都分配一個sessionID。因爲 sessionID 是 Zookeeper 會話的一個重要標識,許多與會話相關的運行機制都是基於這個 sessionID 的,所以,不管是哪臺服務器爲客戶端分配的 sessionID,都務必保證全局惟一。

5.3.1 會話(Session)

在Zookeeper客戶端與服務端成功完成鏈接建立後,就建立了一個會話,Zookeeper會話在整個運行期間的生命週期中,會在不一樣的會話狀態中之間進行切換,這些狀態能夠分爲CONNECTING、CONNECTED、RECONNECTING、RECONNECTED、CLOSE等。

一旦客戶端開始建立Zookeeper對象,那麼客戶端狀態就會變成CONNECTING狀態,同時客戶端開始嘗試鏈接服務端,鏈接成功後,客戶端狀態變爲CONNECTED,一般狀況下,因爲斷網或其餘緣由,客戶端與服務端之間會出現斷開狀況,一旦碰到這種狀況,Zookeeper客戶端會自動進行重連服務,同時客戶端狀態再次變成CONNCTING,直到從新連上服務端後,狀態又變爲CONNECTED,在一般狀況下,客戶端的狀態老是介於CONNECTING 和CONNECTED 之間。可是,若是出現諸如會話超時、權限檢查或是客戶端主動退出程序等狀況,客戶端的狀態就會直接變動爲CLOSE狀態。

5.3.2 會話建立

Session是Zookeeper中的會話實體,表明了一個客戶端會話,其包含了以下四個屬性

  1. sessionID。會話ID,惟一標識一個會話,每次客戶端建立新的會話時,Zookeeper都會爲其分配一個全局惟一的sessionID。
  2. TimeOut。會話超時時間,客戶端在構造Zookeeper實例時,會配置sessionTimeout參數用於指定會話的超時時間,Zookeeper客戶端向服務端發送這個超時時間後,服務端會根據本身的超時時間限制最終肯定會話的超時時間。
  3. TickTime。下次會話超時時間點,爲了便於Zookeeper對會話實行」分桶策略」管理,同時爲了高效低耗地實現會話的超時檢查與清理,Zookeeper會爲每一個會話標記一個下次會話超時時間點,其值大體等於當前時間加上TimeOut。
  4. isClosing。標記一個會話是否已經被關閉,當服務端檢測到會話已經超時失效時,會將該會話的isClosing標記爲」已關閉」,這樣就能確保再也不處理來自該會話的心情求了。

Zookeeper爲了保證請求會話的全局惟一性,在SessionTracker初始化時,調用initializeNextSession方法生成一個sessionID,以後在Zookeeper運行過程當中,會在該sessionID的基礎上爲每一個會話進行分配,初始化算法以下

```
	public static long initializeNextSession(long id) {
	  long nextSid = 0;
	  // 無符號右移8位使爲了不左移24後,再右移8位出現負數而沒法經過高8位肯定sid值
	  nextSid = (System.currentTimeMillis() << 24) >>> 8;
	  nextSid = nextSid | (id << 56);
	  return nextSid;
	}

	```

5.3.3 會話管理

Zookeeper的會話管理主要是經過SessionTracker來負責,其採用了分桶策略(將相似的會話放在同一區塊中進行管理)進行管理,以便Zookeeper對會話進行不一樣區塊的隔離處理以及同一區塊的統一處理。分捅策略

5.4 數據節點 Znode

在Zookeeper中,「節點"分爲兩類,第一類一樣是指構成集羣的機器,咱們稱之爲機器節點;第二類則是指數據模型中的數據單元,咱們稱之爲數據節點一一ZNode。
Zookeeper將全部數據存儲在內存中,數據模型是一棵樹(Znode Tree),由斜槓(/)的進行分割的路徑,就是一個Znode,例如/foo/path1。每一個上都會保存本身的數據內容,同時還會保存一系列屬性信息。
節點

5.4.1 節點類型

在Zookeeper中,node能夠分爲持久節點和臨時節點和順序節點三大類。
能夠經過組合生成以下四種類型節點 
1. PERSISTENT
持久節點,節點建立後便一直存在於Zookeeper服務器上,直到有刪除操做來主動清楚該節點。
2. PERSISTENT_SEQUENTIAL
持久順序節點,相比持久節點,其新增了順序特性,每一個父節點都會爲它的第一級子節點維護一份順序,用於記錄每一個子節點建立的前後順序。在建立節點時,會自動添加一個數字後綴,做爲新的節點名,該數字後綴的上限是整形的最大值。
3.EPEMERAL
臨時節點,臨時節點的生命週期與客戶端會話綁定,客戶端失效,節點會被自動清理。同時,Zookeeper規定不能基於臨時節點來建立子節點,即臨時節點只能做爲葉子節點。
4.EPEMERAL_SEQUENTIAL
臨時順序節點,在臨時節點的基礎添加了順序特性。

5.5 版本——保證分佈式數據原子性操做

每一個數據節點都具備三種類型的版本信息,對數據節點的任何更新操做都會引發版本號的變化。

version– 當前數據節點數據內容的版本號
cversion– 當前數據子節點的版本號
aversion– 當前數據節點ACL變動版本號

上述各版本號都是表示修改次數,如version爲1表示對數據節點的內容變動了一次。即便先後兩次變動並無改變數據內容,version的值仍然會改變。version能夠用於寫入驗證,相似於CAS。

5.6watcher事件監聽器

ZooKeeper容許用戶在指定節點上註冊一些Watcher,當數據節點發生變化的時候,ZooKeeper服務器會把這個變化的通知發送給感興趣的客戶端
監聽

5.7 ACL 權限控制——保障數據的安全

ACL是Access Control Lists 的簡寫, ZooKeeper採用ACL策略來進行權限控制,有如下權限:
CREATE:建立子節點的權限
READ:獲取節點數據和子節點列表的權限
WRITE:更新節點數據的權限
DELETE:刪除子節點的權限
ADMIN:設置節點ACL的權限

5.8 Paxos算法

Paxos算法是基於消息傳遞且具備高度容錯特性的一致性算法,是目前公認的解決分佈式一致性問題最有效的算法之一。(其餘算法有二階段提交、三階段提交等)
篇幅較長 能夠參考https://www.cnblogs.com/linbingdong/p/6253479.html

6、ZooKeeper 能夠作什麼?

  • 分佈式服務註冊與訂閱

    在分佈式環境中,爲了保證高可用性,一般同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就需要在這些對等的服務器中選擇一個來執行相關的業務邏輯,比較典型的服務註冊與訂閱,表明:dubbo。
    註冊中心

  • 分佈式配置中心
    發佈與訂閱模型,即所謂的配置中心,顧名思義就是發佈者將數據發佈到ZK節點上,供訂閱者獲取數據,實現配置信息的集中式管理和動態更新。表明:百度的disconf。
    github:https://github.com/knightliao/disconf

  • 命名服務
    在分佈式系統中,經過使用命名服務,客戶端應用可以根據指定名字來獲取資源或服務的地址,提供者等信息。被命名的實體一般能夠是集羣中的機器,提供的服務地址,進程對象等等——這些咱們均可以統稱他們爲名字(Name)。其中較爲常見的就是一些分佈式服務框架中的服務地址列表。經過調用ZK提供的建立節點的API,可以很容易建立一個全局惟一的path,這個path就能夠做爲一個名稱。
    命名

  • 分佈式鎖
    分佈式鎖,這個主要得益於ZooKeeper爲咱們保證了數據的強一致性。鎖服務能夠分爲兩類,一個是保持獨佔,另外一個是控制時序。
    所謂保持獨佔,就是全部試圖來獲取這個鎖的客戶端,最終只有一個能夠成功得到這把鎖。一般的作法是把zk上的一個znode看做是一把鎖,經過create znode的方式來實現。全部客戶端都去建立 /distribute_lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖。
    控制時序,就是全部視圖來獲取這個鎖的客戶端,最終都是會被安排執行,只是有個全局時序了。作法和上面基本相似,只是這裏 /distribute_lock 已絆預先存在,客戶端在它下面建立臨時有序節點(這個能夠經過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點建立的時序性,從而也造成了每一個客戶端的全局時序

做者:codeyuyu(原文做者)連接:http://www.imooc.com/article/251135

相關文章
相關標籤/搜索