1. 程式人生 > 實用技巧 >zookeeper基礎

zookeeper基礎

摘抄自網上僅用於自己學習https://www.jianshu.com/p/84ad63127cd1

1. Zookeeper簡介

  Zookeeper 是一個開源的 分散式協調服務。設計目的是將那些複雜且容易出錯的分散式一致性服務封裝起來,構成一個高效可靠的原語集,並以一系列簡單易用的介面提供給使用者使用。

  zookeeper 是一個典型的分散式資料一致性的解決方案。分散式應用程式可以基於它實現諸如 資料釋出/訂閱負載均衡命名服務分散式協調/通知叢集管理master 選舉分散式鎖分散式佇列 等功能。

  zookeeper 可以保證如下分散式一致性特性:

  • 順序一致性:從同一個客戶端發起的事務請求,最終將會嚴格按照其發起順序
    被應用到 zookeeper 中
  • 原子性:所有事務請求的結果在叢集中所有機器上的應用情況是一致的,也就是說要麼整個叢集所有機器都成功應用了某一個事務,要麼都沒有應用,一定不會出現叢集中部分機器應用了該事務,而另外一部分沒有應用的情況
  • 單一檢視:無論客戶端連線的是哪個 zookeeper 伺服器,其看到的服務端資料模型都是一致的
  • 可靠性:一旦服務端成功地應用了一個事務,並完成對客戶端的響應,那麼該事務所引起的服務端狀態變更將會被一直保留下來,除非有另個事務又對其進行了改變
  • 實時性:通常人們看到實時性的第一反應是,一旦一個事務被成功應用,那麼客戶端能夠立即從服務端上讀取到這個事務變更後的最新資料狀態。這裡需要注意的是,zookeeper 僅僅保證
    一定的時間段內,客戶端最終一定能夠從服務端上讀取到最新的資料狀態。

2. zookeeper 基本概念

2.1 叢集角色

  在 zookeeper 中,有三種角色:

  • Leader
  • Follower  
  • Observer

  一個 zookeeper 叢集同一時刻只會有一個 Leader,其他都是 Follower 或者 Observer

  zookeeper 配置簡單,每個節點的配置檔案一樣,只有 myid 檔案不一樣。myid 檔案的值必須是 zoo.cfg 中 server.{ 數值 } 的 { 數值 } 部分。myid 檔案在 zoo.cfg 的 dataDir 資料夾下。

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zkcluster/zookeeper3/dataDir
dataLogDir=/opt/zkcluster/zookeeper3/dataLogDir
clientPort=2193
server.1=10.10.130.151:2887:3887
server.2=10.10.130.151:2888:3888
server.3=10.10.130.151:2889:3889

  叢集中連線zookeeper客戶端命令:./zookeeper1/bin/zkCli.sh -server 10.10.130.151:2191

  zookeeper 預設只有 Leader 和 Follower 兩種角色,沒有 Observer 角色。

  為了使用 Observer 模式,在任何想變成 Observer 的節點的配置檔案中加入:peerType=observer 並在所有 server 的配置檔案中,配置成 observer 模式的 server 一行追加 :observer,例如: server.3=10.10.130.151:2889:3889:observer

  zookeeper 叢集的所有機器通過一個 Leader 選舉過程來選定一臺被稱為 Leader 的機器,Leader 伺服器為客戶端提供讀和寫服務

  Follower 和 Observer 都能提供讀服務不能提供寫服務。兩者唯一的區別在於,Observer 機器不參與 Leader 選舉過程,也不參與寫操作的 【過半寫成功】策略,因此,Observer 可以在不影響寫效能的情況下提升叢集的讀效能

2.2 會話(Session)

  Session 是指客戶端會話。zookeeper 中,一個客戶端連線是指客戶端和 zookeeper 伺服器之間的 TCP 長連線。zookeeper 對外的伺服器埠預設是 2181,客戶端啟動時,會首先與伺服器建立一個 TCP 連線,從第一次連線建立開始,客戶端會話的生命週期也開始了,通過這個連線,客戶端能夠通過心跳檢測和伺服器保持有效的會話,也能夠向 zookeeper 伺服器傳送請求並接受響應,同時還能通過該連線接收來自伺服器的 watch 事件通知。Session 的 SessionTimeout 值用來設定一個客戶端會話的超時時間。當由於伺服器壓力太大,網路故障或是客戶端主動斷開連線等各種原因導致客戶端連線斷開時,只要在 SessionTimeout 規定的時間內能夠重新連線上叢集中的任意一臺伺服器,那麼值錢建立的會話仍然有效。

2.3 znode

  zookeeper 會儲存任務的分配、完成情況,等共享資訊,怎麼實現的呢?在 zookeeper 中,這些資訊被儲存在一個個的資料節點上,這些節點被稱為 znode 。它採用了類似檔案系統的層級數狀結構進行管理。

  節點上沒有儲存資料,也有著重要的含義。比如,在主從模式中,當 /master 節點沒有資料時,代表分散式應用的主節點還沒有被選舉出來

  znode 節點儲存的資料為位元組陣列。儲存資料的格式 zookeeper 不做限制,也不提供解析,需要應用自己實現。

  持久節點(persistent)和臨時節點(ephemeral)

  持久節點只能通過 delete 刪除。臨時節點在建立該節點的客戶端崩潰或者關閉時,即客戶端會話失效,那麼這個客戶端建立的所有臨時節點都會被移除。

  有序節點

  znode 可以被設定為有序(sequential)節點。有序 znode 節點被分配唯一一個單調遞增的整數。如果建立了一個有序節點為 /workers/worker-,zookeeper 會自動分配一個序號 1,追加在名字後面,znode 名稱為 /workers/worker-1。通過這種方式,可以建立唯一名稱 znode,並且可以直觀的看到建立的順序。

  znode 支援的操作及暴露的 API:(這裡感覺有問題)

  create /path data # 建立一個名為 /path 的 znode,資料為 data

  delete /path # 刪除名為 /path 的 znode

  exists /path # 檢查是否存在名為 /path 的 znode

  setData /path data # 設定名為 /path 的 znode 的資料為 data

  getData /path # 返回名為 /path 的 znode 的資料

  getChildren /path # 返回所有 /path 節點的所有子節點列表

2.4 版本

  zookeeper 的每一個 znode 上都會儲存資料,對應於每個 znode,zookeeper 都會為其維護一個叫做 Stat 的資料結構,Stat 中記錄了這個 znode 的三個資料版本,分別是 version(當前 znode 版本)、cversion(當前 znode 子節點版本) 和 aversion(當前 znode 的 ACL版本)。

  每個 znode 都有版本號,隨著每次資料變化自增。setData 和 delete,以版本號作為引數,當傳入的版本號和伺服器上不一致時,呼叫失敗。當多個 zookeeper 客戶端同時對一個 znode 操作時,版本將會起到作用,假設 c1,c2 同時往一個 znode 寫資料,c1 先寫完後,版本從 1 升為 2,但是 c2 寫的時候攜帶版本號1,c2 會寫入失敗。

2.5 事務操作

  在 zookeeper 中,能改變 zookeeper 伺服器狀態的操作稱為事務操作。一般包括資料節點建立與刪除、資料內容更新和客戶端會話建立與失效等操作。對應於每一個事務請求,zookeeper 都會為其分配一個全域性唯一的事務 ID,用 ZXID 表示,通常是一個 64 位的數字。每一個 ZXID 對應一次更新操作,從這些 ZXID 中可以間接地識別出 zookeeper 處理這些事務操作請求的全域性順序。

2.5 Watcher

  Watcher(事件監聽器),是 zookeeper 中一個很重要的特性。zookeeper 允許使用者在指定節點上註冊一些 Watcher,並且在一些特定事件被觸發的時候,zookeeper 服務端會將事件通知到感興趣的客戶端上去。該機制是 zookeeper 實現分散式協調服務的重要特性。

2.6 ACL

  zookeeper 採用 ACL(Access Control Lists)策略來進行許可權控制。zookeeper 定義瞭如下 5 種許可權

  • CREATE:建立子節點的許可權
  • READ:獲取節點資料和子節點列表的許可權
  • WRITE:更新節點資料的許可權
  • DELETE:刪除子節點的許可權
  • ADMIN:設定節點 ACL 的許可權

  注意:CREATE 和 DELETE 都是針對子節點的許可權控制

3. ZAB 協議

3.1 ZAB 協議概覽

  zookeeper 使用了一種稱為 Zookeeper Atomic Broadcast(ZAB,Zookeeper 原子廣播協議)的協議作為其資料一致性的核心演算法。

  基於 ZAB 協議,zookeeper 實現了一種主備模式(Leader、Follower)的系統架構來保持叢集中各副本之間資料的一致性。

  具體的,zookeeper 使用了一個單一的主程序(Leader)來接收並處理客戶端的所有事務請求,並採用 ZAB 原子廣播協議,將伺服器資料的狀態變更以事務 Proposal 的形式廣播到所有的副本程序上去(Follower)。ZAB 協議的這個主備模型架構保證了同一時刻叢集中只能有一個主程序來廣播伺服器的狀態變更,因此能夠很好地處理客戶端大量的併發請求。另一方面,考慮到分散式環境中,順序執行的一些狀態變更,其前後會存在一定的依賴關係,有些狀態變更必須依賴於比它早生成的那些狀態變更。這樣的依賴關係也對 ZAB 協議提出一個要求:ZAB 協議必須能夠保證一個全域性的變更序列被順序應用。也就是說,ZAB 協議需要保證,如果一個狀態變更已經被處理了,那麼所有依賴的狀態變更都應該已經被提前處理掉了。最後,考慮主程序在任何時候都有可能出現崩潰退出或者重啟的現象,因此,ZAB 協議還需要做到在當前主程序出現上述異常情況的時候,依然能夠正常工作。

  ZAB 協議的核心是定義了對應那些會改變 zookeeper 伺服器資料狀態的事務請求的處理方式,即:

  所有事務請求必須由一個全域性唯一的伺服器來協調處理,這樣的伺服器被稱為 Leader 伺服器,而剩下的其他伺服器則稱為 Follower 伺服器。Leader 伺服器負責將一個客戶端事務請求轉換成一個事務 Proposal(提案)並將該 Proposal 分發給叢集中所有的 Follower 伺服器。之後 Leader 伺服器需要等待所有 Follower 伺服器的反饋,一旦超過半數的 Follower 伺服器進行了正確的反饋後,Leader 就會再次向所有的 Follower 伺服器分發 Commit 訊息,要求對剛才的 Proposal 進行提交。

  崩潰恢復模式包括兩個階段:Leader 選舉和資料同步

  當叢集中有過半的 Follower 伺服器和 Leader 伺服器的狀態同步,那麼整個叢集就可以進入訊息廣播模式了。

  

3.2 ZAB 協議介紹

  ZAB 協議包括兩種基本的模式,分別是崩潰恢復訊息廣播。在整個 zookeeper 叢集啟動過程中,或是當 Leader 伺服器出現網路中斷、崩潰退出與重啟等異常情況時,ZAB 協議就會進入恢復模式,並選舉產生新的 Leader 伺服器。當選舉產生了新的 Leader 伺服器,同時叢集中有過半的機器與該 Leader 伺服器完成了狀態同步後,ZAB 協議就會退出恢復模式。其中,狀態同步是指資料同步,用來保證叢集中存在過半的機器能夠與 Leader 伺服器的資料狀態保持一致

  

4. Zookeeper 典型應用場景

  zookeeper 是一個高可用的分散式資料管理與協調框架。基於對 ZAB 演算法的實現,該框架能夠很好地保證分散式環境中資料的一致性。使得 zookeeper 成為了解決分散式一致性問題的利器。

4.1 資料釋出與訂閱(配置中心)

  資料釋出與訂閱,即所謂的配置中心,顧名思義就是釋出者將資料釋出到 zookeeper 節點上,供訂閱者進行資料訂閱,進而達到動態獲取資料的目的,實現配置資訊的集中式管理和動態更新

  在我們平常的應用系統開發中,經常會碰到這樣的需求,系統中需要使用一些通用的配置資訊,例如機器列表資訊、資料庫配置資訊等。這些全域性配置資訊通常具備以下 3 個特性。

  • 資料量通常比較小
  • 資料內容在執行時動態變化
  • 叢集中各機器共享,配置一致

  對於這樣的全域性配置資訊就可以釋出到 zookeeper 上,讓客戶端(叢集的機器)去訂閱該訊息。

  釋出/訂閱系統一般有兩種模式,分別是推(Push)和拉(Pull)模式。

  • 推:服務端主動將資料更新發送給所有訂閱的客戶端
  • 拉:客戶端主動發起請求來獲取最新資料,通常客戶端都採用定時輪詢拉取的方式

  zookeeper 採用的是推拉相結合的方式。如下:

  客戶端先在服務端註冊自己需要關注的節點,一旦該節點的資料發生變更,那麼服務端就會向相應的客戶端傳送 watcher 事件通知,客戶端接收到這個訊息通知後,需要主動到服務端獲取最新的資料(推拉結合)。

4.2 命名伺服器(Naming Service)

  命名服務也是分散式系統中比較常見的一類場景。在分散式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址等資訊。被命名的實體通常可以是叢集中的機器,提供的服務,遠端物件等等 —— 這些我們都可以統稱他們為名字(Name)。

  其中比較常見的就是一些分散式伺服器框架(如 RPC、RMI)中的服務地址列表。通過在 zookeeper 裡建立順序節點,能夠很容易建立一個全域性唯一的路徑,這個路徑就可以作為一個名字。

  zookeeper 的命名服務即生成全域性唯一的 ID

  舉個栗子。B 服務部署在六臺伺服器上,存在六個完全不同的 ip 地址,同時 B 服務本身提供一個 dubbo 介面對外,此時,有個 A 服務需要呼叫此介面, 如果提供某一臺伺服器的 ip,則存在該伺服器宕機情況下介面不可用的情況,再切換 ip 就會影響服務的正常使用。此時,可以使用 zookeeper 作為註冊中心,B 服務的六臺伺服器在指定 znode 下建立節點,而 A 服務呼叫之前,先通過指定 znode 的路徑獲取 B 服務的任意子節點中的 ip 資訊,然後通過該 ip 訪問。同時,zookeeper 動態維護這部分節點,定時利用心跳請求檢查 B 服務的伺服器狀態,一旦發現某伺服器無反饋,就刪除節點,防止被 A 服務獲取呼叫。

4.3 分散式協調/通知

  zookeeper 中特有 Watcher 註冊非同步通知機制,能夠很好的實現分散式環境下不同機器,甚至不同系統之間的通知與協調,從而實現對資料變更的實時處理。使用方法通常是不同的客戶端都對 ZK 上同一個 ZNode 進行註冊,監聽 ZNode 的變化(包括 ZNode 本身內容即子節點的),如果 ZNode 發生了變化,那麼所有訂閱的客戶端都能夠接收到相應的 Watcher 通知,並做出相應的處理。

  zk 的分散式協調/通知,是一種通用的分散式系統機器間的通訊方式。

4.4 心跳檢測

  機器間的心跳檢測機制是指在分散式環境中,不同機器(或程序)之間需要檢測到彼此是否在正常執行,例如 A 機器需要知道 B 機器是否正常執行,在傳統的開發中,我們通常是通過主機之間是否可以互相 PING 通來判斷,更復雜一點的話,則會通過在機器之間建立長連線,通過 TCP 連線固有的心跳檢測機制來實現上層機器的心跳檢測,這些都是非常常見的心跳檢測方法。

  ZK 實現分散式機器(程序)間的心跳檢測是基於 ZK 的臨時節點的特性,可以讓不同的程序都在 ZK 的一個指定節點下建立臨時子節點,不同的程序直接可以根據這個臨時子節點來判斷對應的程序是否存活。通過這種方式,檢測和被檢測系統並不需要直接相關聯,大大減少了系統耦合。

4.5 工作進度彙報

  在一個常見的任務分發系統中,通常任務被分發到不同的機器上執行後,需要實時地將自己的任務執行進度彙報給分發系統。這個時候就可以通過 ZK 來實現。在 ZK 上選擇一個節點,每個任務客戶端都在這個節點下面建立臨時子節點,這樣便可以實現兩個功能:

  • 通過判斷臨時節點是否存在來確定任務機器是否存活
  • 各個任務機器會實時地將自己的任務進度寫到這個臨時節點上去,以便中心繫統能夠實時地獲取到任務的執行進度

4.6 Master 選舉

  Master 選舉可以說是 zookeeper 最典型的應用場景了。比如 HDFS 中 Active NameNode 的選舉、YARN 中 Active ResourceManager 的選舉 和 HBase 中 Active HMaster 的選舉等。

  針對 Master 選舉的需求,通常情況下,我們可以選擇常見的關係型資料庫中的主鍵特性來實現:希望成為 Master 的機器都像資料庫中插入一條相同主鍵 ID 的記錄,資料庫會幫我們進行主鍵衝突的檢查,也就是說,只有一臺機器能夠插入成功 —— 那麼,我們就認為向資料庫中成功插入資料的客戶端機器成為 Master。

  依靠關係型資料庫的主鍵特性確實能夠很好地保證在叢集中選舉出唯一的一個 Master。但是,如果當前選舉出的 Master 掛了,該怎麼處理?怎麼知道這個 Master 什麼時候掛了?關係型資料庫無法通知我們這個事件,但是 zookeeper 可以做到。

  利用 zookeeper 的強一致性,能夠很好地保證在分散式高併發情況下節點的建立一定能夠保證全域性唯一性,即 zookeeper 將會保證客戶端無法建立一個已經存在的 ZNode。也就是說,如果同時有多個客戶端請求建立同一臨時節點,那麼最終一定只有一個客戶端請求能夠建立成功。利用這個特性,就能很容易在分散式環境中進行 Master 選舉了。

  成功建立該節點的客戶端所在的機器就成了 Master。同時,其他沒有成功建立該節點的客戶端,都會在該節點上註冊一個子節點變更的 Watcher,用於監控當前 Master 機器是否存活,一旦發現當前的 Master 掛了,那麼其他客戶端將會重新進行 Master 選舉。

  這樣就實現了 Master 的動態選舉。

  

4.7 分散式鎖

  分散式鎖是控制分散式系統之間同步訪問共享資源的一種方式。

  分散式鎖又分為排他鎖共享鎖兩種。

1. 排他鎖

  Exclusive Locks,簡稱 X 鎖,又稱寫鎖獨佔鎖

  如果事務 T1 對資料物件 O1 加上了排他鎖,那麼在整個加鎖期間,只允許事務 T1 對 O1 進行讀取和更新操作,其他任何事務都不能再對這個資料物件進行任何型別的操作(不能再對該物件加鎖),直到 T1 釋放了排他鎖。

  可以看出,排他鎖的核心是如何保證當前只有一個事務獲得鎖,並且鎖被釋放後,所有正在等待獲取鎖的事務都能被通知到。

  如何利用 zookeeper 實現排他鎖?

  定義鎖:zookeeper 上的一個 ZNode 可以表示一個鎖。例如 /exclusive_lock/lock 節點就可以被定義為一個鎖。

  獲得鎖:把 zookeeper 上的一個 ZNode 看作是一個鎖,獲得鎖就通過建立 ZNode 的方式來實現。所有客戶端都去 /exclusive_lock 節點下建立臨時子節點 /exclusive_lock/lock。zookeeper 會保證在所有客戶端中,最終只有一個客戶端能夠建立成功,那麼就可以認為該客戶端獲得了鎖。同時,所有沒有獲取到鎖的客戶端就需要到 /exclusive_lock 節點上註冊一個子節點變更的 Watcher 監聽,以便實時監聽到 lock 節點的變更情況。

  釋放鎖:因為 /exclusive_lock/lock 是一個臨時節點,因此在以下兩種情況下,都有可能釋放鎖。

  • 當前獲得鎖的客戶端機器發生宕機或重啟,那麼該臨時節點就會被刪除,釋放鎖。
  • 正常執行完業務邏輯後,客戶端就會主動將自己建立的臨時節點刪除,釋放鎖。

  無論在什麼情況下移除了 lock 節點,zookeeper 都會通知所有在 /exclusive_lock 節點上註冊了節點變更 Watcher 監聽的客戶端。這些客戶端在接收到通知後,再次重新發起分散式鎖索取。

2. 共享鎖

  Shared Locks,簡稱 S 鎖,又稱為讀鎖。如果事務 T1 對資料物件 O1 加上了共享鎖,那麼 T1 只能對 O1 進行讀操作,其他事務也能同時對 O1 加共享鎖(不能是排他鎖),直到 O1 上的所有共享鎖都釋放後 O1 才能被加排他鎖。

  即:可以多個事務同時獲得一個物件的共享鎖(同時讀),有共享鎖就不能加排他鎖。

  獲取鎖:

  1)客戶端建立一個已經存在的持久節點的臨時順序子節點,然後返回一個帶序號節點名稱;

  2)客戶端獲取這個持久節點的子節點列表

  3)通過子節點列表判斷自己在子節點中的順序,如果是第一個子節點(序號最小),那麼這個客戶端獲取到了鎖;否則判斷自己節點的型別,如果是讀節點,那麼判斷是否有比自己小的寫節點,若沒有,則獲取到了讀鎖,若有,則向比自己小的最後一個寫節點註冊一個 Watcher 監聽;如果是寫節點,那麼向比自己小的最後一個節點註冊一個 Watcher 監聽;

  4)等待 Watcher 通知,如果客戶端建立的是讀節點,則獲取到了讀鎖;否則繼續進行第二步。

  釋放鎖:

  • 因為客戶端建立的節點是一個臨時節點,所以當獲取到鎖的客戶端發生了宕機,zookeeper 上的這個臨時節點會被移除;
  • 獲取到鎖的客戶端,正常執行完業務邏輯後,客戶端會主動將