Zookeeper
服務端
工作機制
Zookeeper從設計模式角度來理解:是一個基於觀察者模式設計的分散式服務管理框架,它負責儲存和管理大家都關心的資料
,然後接受觀察者的註冊
,一旦這些資料的狀態發生變化,Zookeeper 就將負責通知已經在Zookeeper上註冊的那些觀察者
做出相應的反應。
Zookeeper = 檔案系統 + 通知機制
特點
- Zookeeper:一個領導者(Leader),多個跟隨者(Follower)組成的叢集。
- 叢集中只要有
半數以上
節點存活,Zookeeper叢集就能正常服務。所以Zookeeper適合安裝奇數臺伺服器
。 - 全域性資料一致:每個Server儲存一份相同的資料副本,Client無論連線到哪個Server,資料都是一致的。
- 更新請求順序執行,來自同一個Client的更新請求按其傳送順序依次執行。
- 資料更新原子性,一次資料更新要麼成功,要麼失敗。
- 實時性,在一定時間範圍內,Client能讀到最新資料。
資料結構
ZooKeeper 資料模型的結構與 Unix 檔案系統
很類似,整體上可以看作是一棵樹,每個 節點稱做一個 ZNode。每一個 ZNode 預設能夠儲存 1MB
的資料,每個 ZNode 都可以通過其路徑唯一標識
。
圖片
應用場景
提供的服務包括:統一命名服務、統一配置管理、統一叢集管理、伺服器節點動態上下線、軟負載均衡等。
統一命名服務
在分散式環境下,經常需要對應用/服 務進行統一命名,便於識別。
例如:IP不容易記住,而域名容易記住。
統一配置管理
-
分散式環境下,配置檔案同步:
-
一般要求一個叢集中,所有節點的配置資訊是一致的,比如 Kafka 叢集。
-
對配置檔案修改後,希望能夠快速同步到各個 節點上。
-
-
配置管理可交由 Zookeeper 實現:
- 可將配置資訊寫入ZooKeeper上的一個Znode。
- 各個客戶端伺服器監聽這個Znode。
- 一 旦Znode中的資料被修改,ZooKeeper將通知各個客戶端伺服器。
統一叢集管理
- 分散式環境中,實時掌握每個節點的狀態是必要的。
- 可根據節點實時狀態做出一些調整。
- ZooKeeper可以實現實時監控節點狀態變化
- 可將節點資訊寫入ZooKeeper上的一個ZNode。
- 監聽這個ZNode可獲取它的實時狀態變化。
Zookeeper 安裝
下載ZooKeeper:http://pan.baidu.com/s/1pJlwbR9
解壓:tar -zxvf zookeeper-3.4.5.tar.gz 重新命名:mv zookeeper-3.4.5 zk
Zookeeper 引數詳解
# 通訊心跳時間,Zookeeper伺服器與客戶端心跳時間,單位毫秒
tickTime=2000
# LF初始通訊時限
# Leader 和 Follower 初始連線時能容忍的最多心跳數(tickTime 的數量)
initLimit=10
# LF 同步通訊時限
# Leader 和 Follower 之間通訊時間如果超過 syncLimit * tickTime,leader 認為 Follower 死掉,從伺服器列表中刪除 Follower
syncLimit=5
# 儲存 Zookeeper 中的資料
# 預設存放在 tmp 目錄,容易被 Linux 系統定期刪除,所以一般不用預設的 tmp 目錄
dataDir=/opt/module/zookeeper-3.5.7/zkData
# 客戶端連線埠,通常不做修改
clientPort=2181
Zookeeper 叢集配置
配置
新增伺服器編號的配置檔案,在配置檔案中 dataDir=/opt/module/zookeeper-3.5.7/zkData
下建立 myid
檔案,在檔案中新增與 server 對應的編號
。
修改配置檔案,在 bin/zoo_sample.cfg
後面追加對應主機的配置資訊,每一臺主機對應的配置檔案都要進行修改。
server.2=192.168.3.19:2888:3888
server.3=192.168.3.33:2888:3888
server.4=192.168.3.34:2888:3888
配置檔案詳解
server.A=B:C:D
A:是一個資料,表示這是幾號伺服器,對應每臺伺服器建立的 myid
裡面的數字;Zookeeper 啟動時讀取此檔案,拿到裡面的資料與 zoo.cfg 裡面的配置資訊比 較從而判斷到底是哪個 server
。
B:對應的伺服器地址;
C:是這個伺服器 Follower 與叢集中的 Leader 伺服器交換資訊的埠;
D:是萬一叢集中的 Leader 伺服器掛了,需要一個埠來重新進行選舉,選出一個新的 Leader,而這個埠就是用來執行選舉時伺服器相互通訊的埠。
執行
分別啟動對應的 Zookeeper,我這邊配置的是三臺Zookeeper伺服器;
啟動第一臺的 Zookeeper 的狀態:
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Error contacting service. It is probably not running.
此時提示 Error contacting service. It is probably not running.
因為現在還只啟動一臺 Zookeeper,不滿足 半數以上
的節點存活,所以現在不能正常提供服務。
後面依次啟動對應的 Zookeeper 服務。
第二臺啟動與對應的狀態
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
第三臺啟動與對應的狀態
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
第一臺伺服器的狀態
[root@localhost zookeeper-3.5.7]# sh ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
到這裡 Zookeeper 叢集的配置與啟動就搞定了。
Zookeeper 的選舉機制
第一次啟動的時候
以我上面的三個 Zookeeper 伺服器叢集為例:
-
伺服器1啟動,發起一次選舉。伺服器1投自己一票。此時伺服器1票數一票,不夠半數以上(2票),選舉無法完成,伺服器1狀態保持為 LOOKING;
-
伺服器2啟動,再發起一次選舉。伺服器1和2分別投自己一票並交換選票資訊:此時伺服器1發現伺服器2的
myid
比自己目前投票推舉的(伺服器1) 大,更改選票為推舉伺服器2。此時伺服器1票數0票,伺服器2票數2票,此時伺服器2的票數已經超過半數,伺服器2當選Leader。伺服器1更改狀態為FOLLOWING,伺服器2更改狀態為LEADING; -
伺服器3啟動,發起一次選舉。此時伺服器1,2已經不是LOOKING狀態,不會更改選票資訊。交換選票資訊結果:伺服器2為2票,伺服器3為 1票。此時伺服器3服從多數,更改選票資訊為伺服器2,並更改狀態為FOLLOWING;
非第一次啟動的時候
這裡首先還要先講出關於Zookeeper叢集
與Client端
的一些其他概念:
SID:伺服器ID
。用來唯一標識一臺 ZooKeeper叢集中的機器,每臺機器不能重複,和 myid
一致。
ZXID:事務id
。ZXID是一個事務ID,用來標識一次伺服器狀態的變更
。在某一時刻, 叢集中的每臺機器的ZXID值不一定完全一 致,這和 ZooKeeper 伺服器對於客戶端 更新請求
的處理邏輯有關。
Epoch:每個Leader任期的代號
。沒有Leader時同一輪投票過程中的邏輯時鐘值是相同的。每投完一次票這個資料就會增加。
Zookeeper 會發生選舉的場景
- 伺服器初始化啟動(上面描述了)
- 伺服器執行期間無法和 Leader 保持連線
一臺伺服器進入選舉的兩種狀態
-
叢集中本來就已經存在一個 Leader:
機器試圖去選舉Leader時,會被告知當前伺服器的Leader資訊,對於該機器來說,僅僅需要和Leader機器建立連 接,並進行狀態同步即可。
-
叢集中確實不存在 Leader:
假設ZooKeeper由5臺伺服器組成,SID分別為1、2、3、4、5,ZXID分別為8、8、8、7、7,並且此時SID為3的伺服器是Leader。某一時刻, 3和5伺服器出現故障,因此開始進行Leader選舉。
1(EPOCH,ZXID,SID ) 2(EPOCH,ZXID,SID ) 4(EPOCH,ZXID,SID ) SID 為 1、2、4 的機器投票情況 (1,8,1) (1,8,2) (1,7,4) 選舉規則:
- EPOCH大的直接勝出;
- EPOCH相同,事務id大的勝出
- 事務id相同,伺服器id大的勝出;
叢集啟動指令碼
#!/bin/bash
case $1 in
"start"){
for i in 192.168.3.19 192.168.3.33 192.168.3.34
do
echo ----------- zookeeper $i 啟動 -------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
done
}
;;
"stop"){
for i in 192.168.3.19 192.168.3.33 192.168.3.34
do
echo ----------- zookeeper $i 停止 -------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
done
}
;;
"status"){
for i in 192.168.3.19 192.168.3.33 192.168.3.34
do
echo ----------- zookeeper $i 狀態 -------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
done
}
;;
esac
啟動指令碼是建立在機器之前的無密遠端登入設定好的前提下,如果沒有設定好的話,在執行的過程中需要輸入對應的密碼。
[root@localhost ~]# sh zk.sh start
----------- zookeeper 192.168.3.19 啟動 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
----------- zookeeper 192.168.3.33 啟動 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
----------- zookeeper 192.168.3.34 啟動 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost ~]# sh zk.sh status
----------- zookeeper 192.168.3.19 狀態 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
----------- zookeeper 192.168.3.33 狀態 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
----------- zookeeper 192.168.3.34 狀態 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
[root@localhost ~]# sh zk.sh stop
----------- zookeeper 192.168.3.19 停止 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
----------- zookeeper 192.168.3.33 停止 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
----------- zookeeper 192.168.3.34 停止 -------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
客戶端
基本語法
命令基本語法 | 功能描述 |
---|---|
help | 顯示所有指令 |
is path | 使用 ls 命令來檢視當前 znode 的子節點【可監聽】 -w 監聽子節點變化 -s 附加次級資訊 |
create | 普通建立 -s 含有序列 -e 臨時(重啟或者超時消失) |
get path | 獲得節點的值 [可監聽] -w 監聽節點內容變化 -s 附加次級資訊 |
set | 設定節點的具體值 |
stat | 檢視節點的具體值 |
delete | 刪除節點 |
deleteall | 遞迴刪除節點 |
啟動客戶端
先進入 Zookeeper 的安裝目錄:
bin/zkCli.sh -server 192.168.3.34
znode 節點資料資訊
檢視當前 znode 中所有包含的內容
[zk: 192.168.3.34(CONNECTED) 2] ls /
[zookeeper]
檢視當前 znode 中所有包含的詳細內容
[zk: 192.168.3.34(CONNECTED) 3] ls -s /
[zookeeper]cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
引數詳解
czxid:建立節點的事務 zxid
每次修改 ZooKeeper 狀態都會產生一個 ZooKeeper 事務 ID。事務 ID 是 ZooKeeper 中所 有修改總的次序。每次修改都有唯一的 zxid,如果 zxid1 小於 zxid2,那麼 zxid1 在 zxid2 之 前發生。
- ctime:znode 被建立的毫秒數(從 1970 年開始)
- mzxid:znode 最後更新的事務 zxid
- mtime:znode 最後修改的毫秒數(從 1970 年開始)
- pZxid:znode 最後更新的子節點 zxid
- cversion:znode 子節點變化號,znode 子節點修改次數
dataversion:znode 資料變化號
- aclVersion:znode 訪問控制列表的變化號
- ephemeralOwner:如果是臨時節點,這個是 znode 擁有者的 session id。如果不是 臨時節點則是 0。
dataLength:znode 的資料長度
numChildren:znode 子節點數量
節點型別(持久/短暫/有序號/無序號)
簡介
持久(Persistent):客戶端和伺服器端斷開連線後,建立的節點不刪除
短暫(Ephemeral):客戶端和伺服器端斷開連線後,建立的節點自己刪除
(1)持久化目錄節點:客戶端與Zookeeper斷開連線後,該節點依舊存在
(2)持久化順序編號目錄節點:客戶端與Zookeeper斷開連線後,該節點依舊存 在,只是Zookeeper給該節點名稱進行順序編號
(3)臨時目錄節點:客戶端與Zookeeper斷開連線後,該節點被刪除
(4)臨時順序編號目錄節點:客戶端與 Zookeeper 斷開連線後 , 該 節 點 被 刪 除 , 只 是 Zookeeper給該節點名稱進行順序編號。
說明:建立znode時設定順序標識,znode名稱 後會附加一個值,順序號是一個單調遞增的計數 器,由父節點維護;
注意:在分散式系統中,順序號可以被用於 為所有的事件進行全域性排序,這樣客戶端可以通 過順序號推斷事件的順序;
實操
持久節點無序號
建立節點
[zk: 192.168.3.34(CONNECTED) 4] create /lol "luoshou"
Created /lol
[zk: 192.168.3.34(CONNECTED) 5] ls /
[lol, zookeeper]
[zk: 192.168.3.34(CONNECTED) 7] create /lol/noxus "delaiwen"
Created /lol/noxus
[zk: 192.168.3.34(CONNECTED) 8] ls /
[lol, zookeeper]
[zk: 192.168.3.34(CONNECTED) 9] ls /lol
[noxus]
獲取節點的值
[zk: 192.168.3.34(CONNECTED) 11] get -s /lol
luoshou
cZxid = 0x500000002
ctime = Thu Mar 10 23:58:31 CST 2022
mZxid = 0x500000002
mtime = Thu Mar 10 23:58:31 CST 2022
pZxid = 0x500000003
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 1
持久節點有序號
建立節點
[zk: 192.168.3.34(CONNECTED) 1] create -s /lol/demacia "dema"
Created /lol/demacia0000000001
[zk: 192.168.3.34(CONNECTED) 5] create -s /lol/demacia "dema"
Created /lol/demacia0000000002
獲取節點的值
[zk: 192.168.3.34(CONNECTED) 4] ls /lol
[demacia0000000001, noxus]
[zk: 192.168.3.34(CONNECTED) 6] ls /lol
[demacia0000000001, demacia0000000002, noxus]
臨時節點無序號
建立節點
[zk: 192.168.3.34(CONNECTED) 1] create -e /lol/zaun "mengduo"
Created /lol/zaun
獲取節點的值
[zk: 192.168.3.34(CONNECTED) 4] ls /lol
[demacia0000000001, demacia0000000002, noxus, zaun]
臨時節點有序號
建立節點
[zk: 192.168.3.34(CONNECTED) 8] create -e -s /lol/zaun "mengduo"
Created /lol/zaun0000000004
獲取節點的值
[zk: 192.168.3.34(CONNECTED) 11] ls /lol
[demacia0000000001, demacia0000000002, noxus, zaun, zaun0000000004]
退出客戶端後再啟動檢視
重啟後臨時節點已經無了:
[zk: 192.168.3.34(CONNECTED) 0] ls /lol
[demacia0000000001, demacia0000000002, noxus]
修改節點中的值
[zk: 192.168.3.34(CONNECTED) 1] get -s /lol/noxus
delaiwen
cZxid = 0x500000003
ctime = Fri Mar 11 00:00:07 CST 2022
mZxid = 0x500000003
mtime = Fri Mar 11 00:00:07 CST 2022
pZxid = 0x500000003
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0
[zk: 192.168.3.34(CONNECTED) 2] set -s /lol/noxus "luoshou"
cZxid = 0x500000003
ctime = Fri Mar 11 00:00:07 CST 2022
mZxid = 0x50000000f
mtime = Fri Mar 11 00:22:40 CST 2022
pZxid = 0x500000003
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
[zk: 192.168.3.34(CONNECTED) 3] get -s /lol/noxus
luoshou
cZxid = 0x500000003
ctime = Fri Mar 11 00:00:07 CST 2022
mZxid = 0x50000000f
mtime = Fri Mar 11 00:22:40 CST 2022
pZxid = 0x500000003
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
監聽器
簡介
客戶端註冊監聽它關心的目錄節點,當目錄節點發生變化(資料改變、節點刪除、子目 錄節點增加刪除)時,ZooKeeper 會通知客戶端。監聽機制保證 ZooKeeper 儲存的任何的數 據的任何改變都能快速的響應到監聽了該節點的應用程式。
原理
- 首先要有一個 main() 執行緒
- 在 main 執行緒中建立 Zookeeper 客戶端,這時就會建立兩個執行緒,一個負責網路連線通訊(connect),一個負責監聽(listener)
- 通過 connect 執行緒將註冊的監聽事件傳送給 Zookeeper。
- 在 Zookeeper 的註冊監聽器列表中將註冊的監聽事件新增到列表中
- Zookeeper 監聽到有資料或路徑變化,就會將這個訊息傳送給 listener 執行緒
- listener 執行緒內部呼叫了 process() 方法
常見的監聽
-
監聽節點資料的變化
get path [watch]
-
監聽子節點增減的變化
ls path [watch]
實操
節點值變化的監聽
四號機器註冊監聽
[zk: localhost:2181(CONNECTED) 4] get -w /lol
luoshou
三號機器修改對應節點的數值
[zk: 192.168.3.34(CONNECTED) 4] set /lol "delaiwen"
四號機器觸發監聽
[zk: localhost:2181(CONNECTED) 5]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/lol
節點的子節點變化的監聽(路徑變化)
四號機器註冊監聽
[zk: localhost:2181(CONNECTED) 5] ls -w /lol
[demacia0000000001, demacia0000000002, noxus]
三號機器修改對應節點,建立新的子節點
[zk: 192.168.3.34(CONNECTED) 5] create /lol/yordles "timo"
Created /lol/yordles
四號機器觸發監聽
[zk: localhost:2181(CONNECTED) 6]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/lol
注意:註冊一次,只能監聽一次。想再次監聽,需要再次註冊。
節點刪除
# 刪除單節點
[zk: localhost:2181(CONNECTED) 7] delete /lol/yordles
# 刪除有子節點的節點
[zk: localhost:2181(CONNECTED) 9] deleteall /lol