Zookeeper在哪些系統中使用,又是怎麼用的?

Zookeeper似乎被使用得越來越多,而且用法也完全不局限於開始的選主需求,大家總結一下哪些系統使用Zk又是怎麼用的呢


複製一篇之前的筆記過來:

ZooKeeper kazoo

zookeeper作為一個開源的分散式應用協調系統,已經用到了許多分散式項目中,用來完成統一命名服務、狀態同步服務、集群管理、分散式應用配置項的管理等工作。

這裡不再具體提zk的選主邏輯,paxos協議什麼的,只開始講一些用法。

kazoo是一個封裝了zookeeper操作的python庫,其中除了提供底層的zookeeper介面外,還提供了一些更高級別的封裝。

zookeeper的基本操作

zookeeper主要操作分以下幾種:

  1. 創建節點
  2. 讀節點數據
  3. 更新節點數據
  4. 刪除節點
  5. 監控節點變化

其中節點被組織成目錄樹的形式,每個節點下面都可以有一些子節點。

節點可以是以下四種類型:

PERSISTENT:持久化目錄節點,這個目錄節點存儲的數據不會丟失;

PERSISTENT_SEQUENTIAL:順序自動編號的目錄節點,這種目錄節點會根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點名;

EPHEMERAL:臨時目錄節點,一旦創建這個節點的客戶端與伺服器埠也就是 session 超時,這種節點會被自動刪除;

EPHEMERAL_SEQUENTIAL:臨時自動編號節點。

監控節點變化時,可以監控一個節點的變化,也可以監控一個節點所有子節點的變化。

ZK應用

統一命名服務

在分散式系統中,經常需要給一個資源生成一個唯一的ID,在沒有中心管理結點的情況下生成這個ID並不是一件很容易的事兒。zk就提供了這樣一個命名服務。

一般是使用create方法,創建一個自動編號的節點。

配置管理

主要用於多個結點共享配置,並且在配置發生更新時,利用zk可以讓這些使用了這些配置的結點獲得通知,進行重新載入等操作。

集群管理

主要有兩個方面:一是集群選主,二是資源定位。

集群選主是當一個集群會啟動一主一備兩個服務單元時,可以使用zk來選出一個主服務單元。 具體方法就是在一個節點下創建一個自動編號的臨時結點,然後watch父節點,如果該臨時節點成為父節點下編號最小的節點,則認為其成為了主服務單元。

在kazoo中,提供了 election 相關的封裝,使用極其簡單。

zk = KazooClient(hosts="127.0.0.1:2181")
zk.start(10)

election = zk.Election("/electionpath", "my-identifier")

# blocks until the election is won, then calls
# my_leader_function()
election.run(my_leader_function)

在實際使用時有時候會遇到一些工作單元watch集群的master結點,當主從切換時,工作單元可能會需要重新連接到新的主節點以使工作能夠繼續。在kazoo中,暫沒發現有方法能夠直接使孩子watch Election的狀態,暫未試驗是否可以直接使用watch介面直接watch electionpath。不過即使不能通過watch electionpath解決,也可以在master切換之後,通過直接去修改另外一個固定位置的結點而工作單元都watch那個結點來解決。

資源定位主要是用於分散式系統中一些服務節點位置或者狀態發生變化時,通知一些相關的需要知道的服務節點發生了這些變化,以便於其能夠做出一定的響應。比如,一個rpc_server發生了故障遷移,這時就需要client重新能夠發現並向新的地址、埠發起請求。

共享鎖

實現起來和集群選主基本一致,都是創建一個自動編號的臨時結點,然後watch父結點,判斷自己是否是最小編號節點。

在kazoo中提供了 lock 相關封裝:

import os
import sys
import time
from kazoo.client import KazooClient
zk = KazooClient(hosts="127.0.0.1:2181")
zk.start(10)
lock = zk.Lock("/lockpath", "my-identifier")
with lock:
print "got lock"
time.sleep(10)

快速的運行兩遍這個程序,會發現第二次運行的程序會等到第一次運行結束之後才會輸出「got lock」.

隊列管理

...

一些基本用法

關於Watch:

前面講集群資源定位時提到了watch,zk原生的watch都是通過get/get_children/exists等查詢介面提供的,用戶在查詢的時候可以再設置一個watch函數,當有關心的事件觸發時,watch函數會被調用。

例如,get方法中設置的watch函數會在數據發生更新或者刪除時被觸發。exists在節點的存活性發生變化時觸發,而get_children則在子節點的存活性發生變化時觸發。

另外,當watch函數觸發後,用戶需要重新重新設置watch函數,不然隨後的事件再次發生時,將不會被觸發。

仔細思考上述介面會發現,當一次watch事件觸發之後,到再次設置watch函數之間,如果發生了watch的事件,這個事件是不會被觸發的。

雖然表面上這個介面會導致部分watch的事件丟失,但實際上由於設置watch和get/get_children/exists是同一個原子性的操作,故丟失的事件不會影響zookeeper上的數據與client端得到的數據的最終一致性。

使用kazoo的DataWatch可以簡化watch的使用,但有時候可能還是需要去猜測其watch的實現,並不如想像中那麼好用。

個人認為,好多時候,我們其實並不真正的需要watch,輪詢可能會使代碼更為清晰,更少出錯


Leader election

Endpoint discover

Metadata storage

Lock

不要直接用zk api,推薦apache-curator


目前本人有接觸的

HBase是有用zookeeper

選主這功能提主提了就不重複

其他用途

存放hbase的meta data

master要監控某些task的狀態 監控這個動作就是利用zookeeper

例如 distributed log split這個動作,就是master zookeeper regionserver三者協調

master將所有需要split的log列出來

zookeeper將對應一個log表示為一個task 並時刻監控task的狀態

regionserver被master通知去task列表掃 沒被assigned的task 自覺拿回去做

大概就這樣 細講就沒意思

Hadoop架HA也用到zookeeper

其實也類似選主.

簡言

zookeeper類似集群工作的協調者 又可以作為最為集群中存放「全局」變數的角色(不適應存放大量)

不過有必要區別好他跟yarn的角色 開始覺得有點像 後來看深了 其實差別蠻大

如此


所有分散式的協商和一致都可以利用zk實現。可以理解為一個分散式的帶有訂閱功能的小型元資料庫。

使用方式自然就是通過上面這句話而定。比如你需要一個訂閱功能的資料庫,發布你的信息給其他客戶端,享受訂閱功能。又比如你有很多信息是由很多客戶端同時競爭寫入zk,但只允許第一個到達的寫入,就可以享受zk的一致性。


謝邀。用到zk的場景實在太多了,現在流行的分散式系統已經完全無法脫離zookeeper。列舉幾個簡單的例子:

storm中用zookeeper來協調同步集群中機器的狀態(並不傳遞消息)。基本不會有負載,對機器性能沒什麼要求。jstorm也是用zk來做一致性服務。

dubbo中採用zookeeper來作註冊中心,在阿里內部採用的是基於資料庫的註冊中心,我們自己用dubbo的時候採用了zookeeper,配置起來非常方便。當然如果採用redis或者自己寫個簡單的基於內存的註冊中心也是可以的。從穩定性來講,個人覺得zookeeper是比較好的選擇,畢竟zookeeper集群中的機器只要不是半數以上宕掉,服務就是可用的。dubbo服務的消費者和提供者都需要用到zookeeper,然而消費者和提供者之間的長連接建立後,zookeeper參與程度就比較弱了(僅需要接受一些心跳包),除非此時有新的提供者消費者加入或者離開(需要更新節點數據)。因此負載也是非常低的,基本不用考慮性能問題。zookeeper還同時起到了監控各個服務的作用。

HBase要用到zk,這個已經有答主提到,就不用細說了。Hadoop也是需要用到zookeeper的,用來管理集群中的NameNode。

差點忘了,Kafka集群依賴於ZooKeeper。kafka通過zookeeper實現生產者在消費端的負載均衡,動態的集群擴展等等。

最近項目中用到了一個基於redis的高可用緩存方案(支持主從sharding),安裝和客戶端的代碼都參考官方版本的redis+sentinel。感覺sentinel和zookeeper非常相似,不明白為什麼redis官方不直接採用zookeeper作為同步機制。sentinel也是要求最好奇數台機器,也需要做選舉和配置同步。希望有了解的人幫忙科普下這兩者的聯繫和區別:)

PS. 剛查閱了一些文檔,有一些方案是用zk來做Redis高可用的,相比而言,sentinel更輕量一些。

最後真的需要吐槽redis官方為啥不提供redis+sentinel的shared版本的連接池!!!自己寫的和在github上找到的版本都是有問題的,連連踩坑。╮(╯▽╰)╭還好最終順利解決了。


說個在配置管理方向的應用,我們利用Zookeeper實現了配置管理服務QConf,很簡單很小巧的一個項目。

具體的介紹在這裡:從配置文件到分散式配置管理QConf | CatKang的博客

簡單的說:

  • 將配置數據直接存儲在Zookeeper上,不論葉子節點還是中間節點都可以作為配置項。
  • 客戶端機器任何訪問過的配置項都會從Zookeeper上拉取並緩存在本地共享內存,同時註冊Watcher。
  • 之後的配置修改都會收到通知並有機會刷新緩存。

QConf本質上做了兩件事情:

  • 客戶端緩存配置數據
  • 減少連接數

之所以會有QConf這個項目,也之所以知乎上會有當前這個問題,還是由於Zookeeper定位提供更通用,更原子的原材料,留更多的空白和自由給Client。從Zookeeper和Chubby的對比Zookeeper vs Chubby中可以看出,這樣其實提高了使用者的成本和門檻。解決這個問題也是我們和QConf這個項目未來努力的方向。


常用的應用場景:

數據發布訂閱

負載均衡

命名服務

分散式協調通知

集群管理

Master選舉

分散式鎖

分散式隊列

分散式barrier

案例可以看一下dubbo,canal,JStome是怎麼使用zk的。

關於上面的應用場景,可以看看客戶端curator的封裝。

另外,關於zk及zab、paxos演算法,強力推薦《從Paxos到Zookeeper-分散式一致性原理與實踐》。


zookeeper簡介

由於之前了解一個分散式框架(dubbo)時,其中涉及到zookeeper,因此今天就先來大概的介紹一下zookeeper。zookeeper是一個用來管理大量的主機的分散式協調服務。

(一) 分散式應用程序

分散式應用程序可以通過在它們之間協調以完成特定的任務,快速且有效的方式在多個系統中的網路在給定時間(同時)運行

分散式應用程序有兩部分,分別是:伺服器和客戶端應用程序。如下圖所示:

(二) 分散式應用程序的優點

可靠性 可擴展性 透明性

(三) zookeeper提供的服務

命名服務 配置管理 集群管理 節點領導者選舉 鎖定和同步服務 數據註冊表

ZooKeeper基礎

(一) ZooKeeper的體系結構

描繪ZooKeeper 的「客戶端 – 伺服器架構,如下圖所示

ZooKeeper 架構的一部分組件如下表中所解釋。

1. Client:客戶端,發送消息到伺服器。

2. Server:伺服器,ZooKeeper集成的一個節點,提供所有的服務給客戶。

3. 合組:ZooKeeper 伺服器組。

4. Leader:它執行自動恢復,如果任何連接的節點的故障的伺服器節點。

5. Follower:遵循領導指示伺服器節點

(二) 分層命名空間

下圖顯示了用於內存中表示 ZooKeeper 文件系統的樹形結構。 ZooKeeper節點被稱為znode。每個znode由一個名稱識別,並通過路徑(/)序列隔開。

zookeeper名字空間由節點znode構成,其組織方式類似文件系統,其中各個節點相當於目錄和文件,通過路徑作為唯一標識。與文件系統不同的是,每個節點具有與之對應的數據內容,同時也可以具有子節點。在 ZooKeeper 數據模型中每個 znode 維護一個 stat 結構。 一個統計(stat )只是提供了一個 znode 元數據。 它由版本號、動作控制列表(ACL)、時間戳、數據長度組成。

ZooKeeper組件

同一個zookeeper服務下的server有兩種,一種是leader server,另一種是follower server。leader特殊之處在於它有決定權。在zookeeper整個服務下的每台server將複製各個組件。Replicated Database是包含了所有數據的內存資料庫

Zookeeper之leader

讓我們來分析一下一個領導節點在ZooKeeper集合的選舉。考慮集群中有N多的節點。領導人選舉的過程如下

所有節點創建一個順序,znode具有相同路徑,/app/leader/guid_。

ZooKeeper 的集合將追加的10位序列號的路徑。

對於給定的實例,它在znode創建最小數量的節點成為領導者以及所有其他節點的追隨者。

每一個追隨者節點監控下一個最小號的znode。

Zookeeper安裝配置

(一) 安裝Java(略)

(二) ZooKeeper框架的安裝

1. 下載並tar開解壓(略)

2. 創建配置文件

打開 並編輯conf/zoo.cfg 配置文件,並將以下所有參數設置為開始點。

tickTime = 2000

dataDir = /path/to/zookeeper/data

clientPort = 2181

initLimit = 10

syncLimit = 5

3. 啟動ZooKeeper伺服器

$ bin/zkServer.sh start

4. 啟動 CLI

$ bin/zkCli.sh

5. 停止ZooKeeper伺服器

$ bin/zkServer.sh stop

Zookeeper CLI

ZooKeeper 命令行界面(CLI)是用來與 ZooKeeper 集成作開發進行交互的。這是在調試和使用不同的選項時的工作有用。

為了執行ZooKeeper的CLI操作, ZooKeeper伺服器首先要啟動 (「bin/zkServer.sh start」) , 然後使用 ZooKeeper 客戶端 (「bin/zkCli.sh」). 當客戶端啟動後,可以執行以下操作:(1)創建znodes,(2)獲取數據,(3)監視 znode 變化,(4)設置數據,(5)創建 znode 的子 znode,(6)列出一個 znode 的子 znode,(7)檢查狀態,(8)刪除一個 znode

(一) 創建Znodes

create /path /data

(二) 獲取數據

get /path

(三) 監視

get /path [watch] 1

(四) 設置數據

set /path /data

(五) 創建子znode

create /parent/path/subnode/path /data

(六) 列出子znode

ls /path

(七) 檢查狀態

stat /path

(八) 刪除Znode

rmr /path

Zookeeper 常用API

ZooKeeper有一個Java和C綁定的官方API。ZooKeeper社區提供了對於大多數語言(.NET,Python等)的非官方API。使用ZooKeeper的API,應用程序可以連接,互動,操作數據,協調,以及從ZooKeeper集成斷開。

(一) ZooKeeper的API基礎知識

客戶端應遵循下面給出帶 ZooKeeper 集成一個清晰的交互步驟。

連接到ZooKeeper 。ZooKeeper 集成分配客戶端的會話ID。

定期發送心跳到伺服器。否則,ZooKeeper 集成過期的會話ID,那麼客戶端需要重新連接。

獲得/設置只要znodes會話ID是活動的。

從 ZooKeeper 集成斷開,當所有的任務都完成後。如果客戶端處於非活動狀態較長時間,那麼 ZooKeeper 集成會自動斷開客戶機。

(二) Java綁定

讓我們這一章中理解最重要的ZooKeeper API。ZooKeeper API的中心部分是ZooKeeper 類。它提供了一些選項來連接 ZooKeeper 集成在其構造,有以下幾種方法

? connect ? 連接到 ZooKeeper 的集成

? create ? 創建一個 znode

? exists ? 檢查znode是否存在及其信息

? getData ? 從一個特定的znode獲取數據

? setData ? 設置數據在特定znode

? getChildren ? 得到一個特定 znode 的所有可用子節點

? delete ? 得到一個特定的 znode 及其所有子節點

? close ? 關閉連接

(三) 連接到 ZooKeeper 集合

ZooKeeper類通過它的構造函數提供了連接功能。構造函數如下:

ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)

(四) 創建一個Znode

ZooKeeper類提供了一個方法來在集合 ZooKeeper 創建一個新的 znode。創建方法如下:

create(String path, byte[] data, List& acl, CreateMode createMode)

(五) Exists – 檢查一個Znode的存在

exists 方法來檢查 znode 的存在。如果指定的 znode 存在它返回一個 znode 元數據。exists 方法如下

exists(String path, boolean watcher)

(六) getData 方法

getData方法來獲取連接在指定 znode 及其狀態的數據。getData方法如下

getData(String path, Watcher watcher, Stat stat)

(七) setData 方法

SetData方法來修改附著在指定 znode 的數據。SetData方法如下

setData(String path, byte[] data, int version)

(八) getChildren 方法

getChildren方法來得到一個特定的 znode 所有子節點。getChildren 方法如下

getChildren(String path, Watcher watcher)

(九) 刪除一個Znode

delete 方法來刪除指定 znode。delete方法如下

delete(String path, int version)

更多精彩內容請移步Cobub Razor官網博客


大的不說了,很多分散式系統都需要基於zk,比如kafka、HBase 之類的。

說說小的,比如:分散式鎖。在集群中,一把好鎖必不可少吧,是自己搭一個呢?還是美美的用Curator提供的各種鎖好呢?

再比如做HA,zk的臨時(Ephemeral)節點天生就為主從切換準備的,這裡keepalive之類的通通退下吧。

回歸最最基本的,下配置,在你的集群里,不用zk還能用啥呢?兼備性能和高可用啊~!


Zookeeper會在很多場景中用到,我在這隻簡單說一下在Hadoop和HBase中的使用。

ZooKeeper是一個開源的分散式協調服務,由雅虎創建,是GoogleChubby的開源實現。分散式應用程序可以基於ZooKeeper實現諸如數據發布/訂閱、負載均衡、命名服務、分散式協調/通知、集群管理、Master選舉、分散式鎖和分散式隊列等功能。

在ZooKeeper中,有三種角色:

Leader

Follower

Observer

一個ZooKeeper集群同一時刻只會有一個Leader,其他都是Follower或Observer。ZooKeeper配置很簡單,每個節點的配置文件(zoo.cfg)都是一樣的,只有myid文件不一樣。myid的值必須是zoo.cfg中server.{數值}的{數值}部分。在Hadoop中,ZooKeeper主要用於實現HA(Hive Availability),包括HDFS的NamaNode和YARN的ResourceManager的HA。同時,在YARN中,ZooKeepr還用來存儲應用的運行狀態。HDFS的NamaNode和YARN的ResourceManager利用ZooKeepr實現HA的原理是一樣的YARN主要由ResourceManager(RM)、NodeManager(NM)、ApplicationMaster(AM)和Container四部分組成。其中最核心的就是ResourceManager。ResourceManager負責集群中所有資源的統一管理和分配,同時接收來自各個節點(NodeManager)的資源彙報信息,並把這些信息按照一定的策略分配給各個應用程序(ApplicationManager),其內部維護了各個應用程序的ApplicationMaster信息、NodeManager信息以及資源使用信息等。

為了實現HA,必須有多個ResourceManager並存(一般就兩個),並且只有一個ResourceManager處於Active狀態,其他的則處於Standby狀態,當Active節點無法正常工作(如機器宕機或重啟)時,處於Standby的就會通過競爭選舉產生新的Active節點。

ZooKeepr在Hadoop中的應用主要有:

HDFS中NameNode的HA和YARN中ResourceManager的HA。

存儲RMStateStore狀態信息

HBase主要用ZooKeeper來實現HMaster選舉與主備切換、系統容錯、RootRegion管理、Region狀態管理和分散式SplitWAL任務管理等。

HMaster選舉與主備切換

HMaster選舉與主備切換的原理和HDFS中NameNode及YARN中ResourceManager的HA原理相同。

系統容錯

當HBase啟動時,每個RegionServer都會到ZooKeeper的/hbase/rs節點下創建一個信息節點(下文中,我們稱該節點為」rs狀態節點」),例如/hbase/rs/[Hostname],同時,HMaster會對這個節點註冊監聽。當某個RegionServer 掛掉的時候,ZooKeeper會因為在一段時間內無法接受其心跳(即 Session 失效),而刪除掉該 RegionServer 伺服器對應的 rs 狀態節點。與此同時,HMaster 則會接收到 ZooKeeper 的 NodeDelete 通知,從而感知到某個節點斷開,並立即開始容錯工作。

HBase為什麼不直接讓HMaster來負責RegionServer的監控呢?如果HMaster直接通過心跳機制等來管理RegionServer的狀態,隨著集群越來越大,HMaster的管理負擔會越來越重,另外它自身也有掛掉的可能,因此數據還需要持久化。在這種情況下,ZooKeeper就成了理想的選擇。

RootRegion管理

對應HBase集群來說,數據存儲的位置信息是記錄在元數據region,也就是RootRegion上的。每次客戶端發起新的請求,需要知道數據的位置,就會去查詢RootRegion,而RootRegion自身位置則是記錄在ZooKeeper上的(默認情況下,是記錄在ZooKeeper的/hbase/meta-region-server節點中)。當RootRegion發生變化,比如Region的手工移動、重新負載均衡或RootRegion所在伺服器發生了故障等是,就能夠通過ZooKeeper來感知到這一變化並做出一系列相應的容災措施,從而保證客戶端總是能夠拿到正確的RootRegion信息。

Region管理

HBase里的Region會經常發生變更,這些變更的原因來自於系統故障、負載均衡、配置修改、Region分裂與合併等。一旦Region發生移動,它就會經歷下線(offline)和重新上線(online)的過程。

在下線期間數據是不能被訪問的,並且Region的這個狀態變化必須讓全局知曉,否則可能會出現事務性的異常。對於大的HBase集群來說,Region的數量可能會多達十萬級別,甚至更多,這樣規模的Region狀態管理交給ZooKeeper來做也是一個很好的選擇。

分散式SplitWAL任務管理

當某台RegionServer伺服器掛掉時,由於總有一部分新寫入的數據還沒有持久化到HFile中,因此在遷移該RegionServer的服務時,一個重要的工作就是從WAL中恢復這部分還在內存中的數據,而這部分工作最關鍵的一步就是SplitWAL,即HMaster需要遍歷該RegionServer伺服器的WAL,並按Region切分成小塊移動到新的地址下,並進行日誌的回放(replay)。

由於單個RegionServer的日誌量相對龐大(可能有上千個Region,上GB的日誌),而用戶又往往希望系統能夠快速完成日誌的恢復工作。因此一個可行的方案是將這個處理WAL的任務分給多台RegionServer伺服器來共同處理,而這就又需要一個持久化組件來輔助HMaster完成任務的分配。當前的做法是,HMaster會在ZooKeeper上創建一個SplitWAL節點(默認情況下,是/hbase/SplitWAL節點),將「哪個RegionServer處理哪個Region」這樣的信息以列表的形式存放到該節點上,然後由各個RegionServer伺服器自行到該節點上去領取任務並在任務執行成功或失敗後再更新該節點的信息,以通知HMaster繼續進行後面的步驟。ZooKeeper在這裡擔負起了分散式集群中相互通知和信息持久化的角色。

小結:以上就是一些HBase中依賴ZooKeeper完成分散式協調功能的典型場景。但事實上,HBase對ZooKeepr的依賴還不止這些,比如HMaster還依賴ZooKeeper來完成Table的enable/disable狀態記錄,以及HBase中幾乎所有的元數據存儲都是放在ZooKeeper上的。


不說啥了,基於zk的分散式配置平台wenbo2018/Jconfwenbo2018/Jconf,分散式RPC框架利用zk作為註冊中心wenbo2018/foxwenbo2018/fox


推薦閱讀:

分散式計算框架 Hadoop 為什麼叫 "Hadoop" ?
演算法研發工作中對於MPI和Spark的一些困惑?
做大數據相關專業,如Hadoop、Hive、Impala等研究與優化的人就業情況如何呢?
如何進入大數據領域,學習路線是什麼?
大數據,hadoop,spark,mahout的學習路線和需要的基礎知識,以及掌握到什麼程度?

TAG:Hadoop | 分散式系統 | ZooKeeper |