1. 程式人生 > >zookeeper1:原理和使用場景

zookeeper1:原理和使用場景

0. zookeeper是什麼

 

zookeeper是一個分散式服務框架,主要用來進行分散式叢集的管理以及解決分散式系統中資料一致性的問題

zookeeper能夠提供類似於檔案系統樹形目錄結構的資料儲存,並維護和監控目錄結構以及資料的變化,一旦被監視的資訊發生變化,zookeeper會自動把變化資訊同步給對此處變化感興趣的客戶端,所以大體上來說zookeeper=檔案系統+通知機制

 

1. zookeeper原理

 

檔案系統結構

下圖顯示了用於記憶體中表示 ZooKeeper 檔案系統的樹形結構。 ZooKeeper節點被稱為znode。每個znode由一個名稱識別,並通過路徑(/)序列隔開,需要注意的是訪問路徑只支援絕對路徑,不支援相對路徑,所以所有涉及到路勁的地方都需要從根節點開始寫

 

         

Znodes 型別

Znodes 根據分類方法的不同,可以有兩種分類方法,分別是永續性和短暫行,以及連續型和非連續型,所以排列組合共有4種類型

  • 永續性znode − 永續性 znode 處於活動狀態,即使客戶端,它創造了特定的 znode。預設情況下,所有的 znodes 是持久的,除非另有說明。
  • 短暫znode − 短暫znodes活躍,直到客戶端還活著。當客戶端被從 ZooKeeper 集合斷開連線,然後znodes自動刪除。由於這個原因,只有短暫znodes不允許再有一個子。如果短週期znode被刪除,那麼下一個合適的節點,將填補其位置。短暫znodes 發揮在領導選舉中起重要作用。
  • 連續持久znode和連續短暫znode − 連續znodes可以是持久或短暫的。當一個新的znode作為連續znode建立的,則 ZooKeeper 通過將10位的序列號為原始名稱設定znode的路徑。例如,如果使用路徑 /myapp 來建立一個znode作為連續znode,ZooKeeper將改變路徑 /myapp0000000001並設定一個序列號為0000000002。如果兩個連續znodes同時被建立,ZooKeeper從來不使用相同數量在每個znode上。連續znodes在鎖定和同步中起到重要作用。

 

watcher機制

可以說zookeeper能夠實現通知機制的原因就是因為watcher機制了,這也是zookeeper的核心功能,通過註冊watcher到指定的節點上,在節點產生變化的時候我們就會收到通知

能夠監控到的變化如下:

事件(跟Znode節點相關的):

EventType.NodeCreated

EventType.NodeDataChanged

EventType.NodeChildrenChanged

EventType.NodeDeleted

EventType.NONE  ---連線上zk後觸發此事件型別

狀態(跟客戶端例項相關的):

KeeperState.Disconnected

KeeperState.SyncConnected

KeeperState.AuthFailed

KeeperState.Expired

 

2. ZooKeeper典型使用場景

 

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

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

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

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

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

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

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

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

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

3.2 命名服務(Naming Service)

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

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

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

3.3 分散式協調/通知

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

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

3.3.1 心跳檢測

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

下面來看看如何使用ZK來實現分散式機器(程序)間的心跳檢測。

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

3.3.2 工作進度彙報

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

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

3.4 Master選舉

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

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

依靠關係型資料庫的主鍵特性確實能夠很好地保證在叢集中選舉出唯一的一個Master。

但是,如果當前選舉出的 Master 掛了,那麼該如何處理?誰來告訴我 Master 掛了呢?顯然,關係型資料庫無法通知我們這個事件。但是,ZooKeeper 可以做到!

利用 ZooKeepr 的強一致性,能夠很好地保證在分散式高併發情況下節點的建立一定能夠保證全域性唯一性,即 ZooKeeper 將會保證客戶端無法建立一個已經存在的 ZNode。

也就是說,如果同時有多個客戶端請求建立同一個臨時節點,那麼最終一定只有一個客戶端請求能夠建立成功。利用這個特性,就能很容易地在分散式環境中進行 Master 選舉了。

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

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

3.5 分散式鎖

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

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

3.5.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 監聽的客戶端。這些客戶端在接收到通知後,再次重新發起分散式鎖獲取,即重複『獲取鎖』過程。

3.5.2 共享鎖

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

總結:可以多個事務同時獲得一個物件的共享鎖(同時讀),有共享鎖就不能再加排他鎖(因為排他鎖是寫鎖)