Zookeeper 簡介
ZooKeeper是一個分布式的,開放源碼的分布式應用程式協調服務,是Google的Chubby一個開源的實作,是Hadoop和Hbase的重要元件。它是一個為分布式應用提供一緻性服務的軟體,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。
ZooKeeper的目标就是封裝好複雜易出錯的關鍵服務,将簡單易用的接口和性能高效、功能穩定的系統提供給使用者。
ZooKeeper包含一個簡單的原語集, [1] 提供Java和C的接口。
ZooKeeper代碼版本中,提供了分布式獨享鎖、選舉、隊列的接口,代碼在zookeeper-3.4.3\src\recipes。其中分布鎖和隊列有Java和C兩個版本,選舉隻有Java版本。
原理
ZooKeeper是以Fast Paxos算法為基礎的,Paxos 算法存在活鎖的問題,即當有多個proposer交錯送出時,有可能互相排斥導緻沒有一個proposer能送出成功,而Fast Paxos作了一些優化,通過選舉産生一個leader (上司者),隻有leader才能送出proposer,具體算法可見Fast Paxos。是以,要想弄懂ZooKeeper首先得對Fast Paxos有所了解。 [3]
ZooKeeper的基本運轉流程:
- 選舉Leader。
- 同步資料。
- 選舉Leader過程中算法有很多,但要達到的選舉标準是一緻的。
- Leader要具有最高的執行ID,類似root權限。
- 叢集中大多數的機器得到響應并接受選出的Leader。
介紹一下 ZooKeeper 的幾個核心概念,是以有必要預先了解這些概念
叢集角色,在Zookeeper中有三種角色:
- Leader
- Follower
- Observer
一個 ZooKeeper 叢集同一時刻隻會有一個 Leader,其他都是 Follower 或 Observer。
ZooKeeper 配置很簡單,每個節點的配置檔案(zoo.cfg)都是一樣的,隻有 myid 檔案不一樣。myid 的值必須是 zoo.cfg中server.{數值} 的{數值}部分。
zoo.cfg配置檔案示例
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
在裝有 ZooKeeper 的機器的終端執行 zookeeper-server status 可以看目前節點的 ZooKeeper是什麼角色(Leader or Follower)。
ZooKeeper 預設隻有 Leader 和 Follower 兩種角色,沒有 Observer 角色。為了使用 Observer 模式,在任何想變成Observer的節點的配置檔案中加入:peerType=observer 并在所有 server 的配置檔案中,配置成 observer 模式的 server 的那行配置追加 :observer
節點讀寫服務分工
1.ZooKeeper 叢集的所有機器通過一個 Leader 選舉過程來標明一台被稱為『Leader』的機器,Leader伺服器為用戶端提供讀和寫服務。
2.Follower 和 Observer 都能提供讀服務,不能提供寫服務。兩者唯一的差別在于,Observer機器不參與 Leader 選舉過程,也不參與寫操作的『過半寫成功』政策,是以 Observer 可以在不影響寫性能的情況下提升叢集的讀性能。
Session
Session 是指用戶端會話,在講解用戶端會話之前,我們先來了解下用戶端連接配接。在ZooKeeper 中,一個用戶端連接配接是指用戶端和 ZooKeeper 伺服器之間的TCP長連接配接。
ZooKeeper 對外的服務端口預設是2181,用戶端啟動時,首先會與伺服器建立一個TCP連接配接,從第一次連接配接建立開始,用戶端會話的生命周期也開始了,通過這個連接配接,用戶端能夠通過心跳檢測和伺服器保持有效的會話,也能夠向 ZooKeeper 伺服器發送請求并接受響應,同時還能通過該連接配接接收來自伺服器的 Watch 事件通知。
Session 的 SessionTimeout 值用來設定一個用戶端會話的逾時時間。當由于伺服器壓力太大、網絡故障或是用戶端主動斷開連接配接等各種原因導緻用戶端連接配接斷開時,隻要在 SessionTimeout 規定的時間内能夠重新連接配接上叢集中任意一台伺服器,那麼之前建立的會話仍然有效。
資料節點
zookeeper的結構其實就是一個樹形結構,leader就相當于其中的根結點,其它節點就相當于follow節點,每個節點都保留自己的内容。
zookeeper的節點分兩類:持久節點和臨時節點
- 持久節點:
所謂持久節點是指一旦這個樹形結構上被建立了,除非主動進行對樹節點的移除操作,否則這個 節點将一直儲存在 ZooKeeper 上。
- 臨時節點:
臨時節點的生命周期跟用戶端會話綁定,一旦用戶端會話失效,那麼這個用戶端建立的所有臨時節點都會被移除。
狀态資訊
每個 節點除了存儲資料内容之外,還存儲了 節點本身的一些狀态資訊。用 get 指令可以 同時獲得某個 節點的内容和狀态資訊 在 ZooKeeper 中,version 屬性是用來實作樂觀鎖機制中的『寫入校驗』的(保證分布 式資料原子性操作)。
事物操作
在ZooKeeper中,能改變ZooKeeper伺服器狀态的操作稱為事務操作。一般包括資料節點建立與删除、資料内容更新和用戶端會話建立與失效等操作。對應每一個事務請求,ZooKeeper都會為其配置設定一個全局唯一的事務ID,用 ZXID 表示,通常是一個64位的數字。每一個 ZXID對應一次更新操作,從這些 ZXID 中可以間接地識别出 ZooKeeper處理這些事務操作請求的
全局順序。
Watcher(事件監聽器)
ZooKeeper 中一個很重要的特性。ZooKeeper允許使用者在指定節點上注冊一些 Watcher, 并且在一些特定事件觸發的時候,ZooKeeper 服務端會将事件通知到感興趣的用戶端上去。該 機制是 ZooKeeper 實作分布式協調服務的重要特性。
ZooKeeper應用的典型場景
ZooKeeper 是一個高可用的分布式資料管理與協調架構。基于對ZAB算法的實作,該架構能夠很好地保證分布式環境中資料的一緻性。也是基于這樣的特性,使得 ZooKeeper 成為了解決分布式一緻性問題的利器。
1 . 資料釋出與訂閱(配置中心)
資料釋出與訂閱,即所謂的配置中心,顧名思義就是釋出者将資料釋出到 ZooKeeper 節點上,供訂閱者進行資料訂閱,進而達到動态擷取資料的目的,實作配置資訊的集中式管理和動态更新。
對于:資料量通常比較小。資料内容在運作時動态變化。叢集中各機器共享,配置一緻。這樣的全局配置資訊就可以釋出到 ZooKeeper上,讓用戶端(叢集的機器)去訂閱該消息。
釋出/訂閱系統一般有兩種設計模式,分别是推(Push)和拉(Pull)模式。
- 推模式
服務端主動将資料更新發送給所有訂閱的用戶端
- 拉模式
用戶端主動發起請求來擷取最新資料,通常用戶端都采用定時輪詢拉取的方式
ZooKeeper 采用的是推拉相結合的方式:
用戶端想服務端注冊自己需要關注的節點,一旦該節點的資料發生變更,那麼服務端就會向相應
的用戶端發送Watcher事件通知,用戶端接收到這個消息通知後,需要主動到服務端擷取最新的資料
2 . 命名服務
命名服務也是分布式系統中比較常見的一類場景。在分布式系統中,通過使用命名服務,用戶端應用能夠根據指定名字來擷取資源或服務的位址,提供者等資訊。被命名的實體通常可以是叢集中的機器,提供的服務,遠端對象等等——這些我們都可以統稱他們為名字。
其中較為常見的就是一些分布式服務架構(如RPC)中的服務位址清單。通過在ZooKeepr裡
建立順序節點,能夠很容易建立一個全局唯一的路徑,這個路徑就可以作為一個名字。
ZooKeeper 的命名服務即生成全局唯一的ID。
3 . 分布式協調服務/通知
ZooKeeper 中特有 Watcher 注冊與異步通知機制,能夠很好的實作分布式環境下不同機器,
甚至不同系統之間的通知與協調,進而實作對資料變更的實時處理。使用方法通常是不同的用戶端
如果 機器節點 發生了變化,那麼所有訂閱的用戶端都能夠接收到相應的Watcher通知,并做出相應
的處理。
ZooKeeper的分布式協調/通知,是一種通用的分布式系統機器間的通信方式。
4 . Master選舉
Master 選舉可以說是 ZooKeeper 最典型的應用場景了。比如 HDFS 中 Active NameNode 的選舉、YARN 中 Active ResourceManager 的選舉和 HBase 中 Active HMaster 的選舉等。
針對 Master 選舉的需求,通常情況下,我們可以選擇常見的關系型資料庫中的主鍵特性來
實作:希望成為 Master 的機器都向資料庫中插入一條相同主鍵ID的記錄,資料庫會幫我們進行
主鍵沖突檢查,也就是說,隻有一台機器能插入成功——那麼,我們就認為向資料庫中成功插入資料
的用戶端機器成為Master。
依靠關系型資料庫的主鍵特性确實能夠很好地保證在叢集中選舉出唯一的一個Master。
但是,如果目前選舉出的 Master 挂了,那麼該如何處理?誰來告訴我 Master 挂了呢?
顯然,關系型資料庫無法通知我們這個事件。但是,ZooKeeper 可以做到!
利用 ZooKeepr 的強一緻性,能夠很好地保證在分布式高并發情況下節點的建立一定能夠
保證全局唯一性,即 ZooKeeper 将會保證用戶端無法建立一個已經存在的 資料單元節點。
也就是說,如果同時有多個用戶端請求建立同一個臨時節點,那麼最終一定隻有一個用戶端
請求能夠建立成功。利用這個特性,就能很容易地在分布式環境中進行 Master 選舉了。
成功建立該節點的用戶端所在的機器就成為了 Master。同時,其他沒有成功建立該節點的
用戶端,都會在該節點上注冊一個子節點變更的 Watcher,用于監控目前 Master 機器是否存
活,一旦發現目前的Master挂了,那麼其他用戶端将會重新進行 Master 選舉。
這樣就實作了 Master 的動态選舉。
5 . 分布式鎖
分布式鎖是控制分布式系統之間同步通路共享資源的一種方式
分布式鎖又分為排他鎖和共享鎖兩種
排它鎖
ZooKeeper如何實作排它鎖?
定義鎖
ZooKeeper 上的一個 機器節點 可以表示一個鎖
獲得鎖
把ZooKeeper上的一個節點看作是一個鎖,獲得鎖就通過建立臨時節點的方式來實作。
ZooKeeper 會保證在所有用戶端中,最終隻有一個用戶端能夠建立成功,那麼就可以
認為該用戶端獲得了鎖。同時,所有沒有擷取到鎖的用戶端就需要到/exclusive_lock
節點上注冊一個子節點變更的Watcher監聽,以便實時監聽到lock節點的變更情況。
釋放鎖
因為鎖是一個臨時節點,釋放鎖有兩種方式
目前獲得鎖的用戶端機器發生當機或重新開機,那麼該臨時節點就會被删除,釋放鎖
正常執行完業務邏輯後,用戶端就會主動将自己建立的臨時節點删除,釋放鎖。
無論在什麼情況下移除了lock節點,ZooKeeper 都會通知所有在 /exclusive_lock 節點上注冊了節點變更 Watcher 監聽的用戶端。這些用戶端在接收到通知後,再次重新發起分布式鎖擷取,即重複『擷取鎖』過程。
共享鎖
共享鎖在同一個程序中很容易實作,但是在跨程序或者在不同 Server 之間就不好實作了。Zookeeper 卻很容易實作這個功能,實作方式也是需要獲得鎖的 Server 建立一個 EPHEMERAL_SEQUENTIAL 目錄節點,然後調用 getChildren方法擷取目前的目錄節點清單中最小的目錄節點是不是就是自己建立的目錄節點,如果正是自己建立的,那麼它就獲得了這個鎖,如果不是那麼它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點清單的變化,一直到自己建立的節點是清單中最小編号的目錄節點,進而獲得鎖,釋放鎖很簡單,隻要删除前面它自己所建立的目錄節點就行了。
總結
本文介紹的 Zookeeper 的基本知識,以及介紹了幾個典型的應用場景。這些都是 Zookeeper的基本功能,最重要的是 Zoopkeeper 提供了一套很好的分布式叢集管理的機制,就是它這種基于層次型的目錄樹的資料結構,并對樹中的節點進行有效管理,進而可以設計出多種多樣的分布式的資料管理模型,而不僅僅局限于上面提到的幾個常用應用場景