1. 程式人生 > >分散式服務管理框架-Zookeeper客戶端zkCli.sh使用詳解

分散式服務管理框架-Zookeeper客戶端zkCli.sh使用詳解

在學習zookeeper(下面簡稱zk)客戶端之前,有必要先了解一下zk的資料模型。zk維護著一個邏輯上的樹形層次結構,樹中的節點稱為znode,和Linux系統的檔案系統結構非常相似,如下圖所示:
Zookeeper資料結構
這種資料結構有如下特點:
1> 每個znode都有唯一路徑標識,最頂層的znode為/,比如p_2這個znode的路徑標識為/app1/p_2,znode只支援絕對路徑,不支援相對路徑,也不支援“.”和“..”
2> znode可以有子節點,並且每個znode可以儲存資料。但zk是被設計用來協調管理服務的,因此znode裡儲存的都是一些小資料,而不是大容量的資料,資料容量一般在1M範圍內。
3> znode的資料有版本號,可以用在併發訪問場景中,用樂觀鎖機制實現資料的一致性
4> znode分為臨時節點和永久節點,zk的客戶端和伺服器通訊採用長連線的方式,每個客戶端和伺服器通過心跳來保持連線,這個連線狀態稱為session,如果znode是臨時節點,當session失效(即客戶端與伺服器斷開連線),znode會被伺服器自動刪除。
5> znode的節點名稱可以自動編號,如果app1已經存在,再建立的話,將會自動命名為app2,這種節點稱為序列節點。
6> znode可以被監控,包括這個節點中儲存的資料被修改、子節點列表變化(刪除或新增子節點)等,一旦變化,zk伺服器會通過所有監控該節點的客戶端,這是zk的核心特性,zk很多的功能都是基於這個特性實現的。

zkCli.sh指令碼是Zookeeper安裝包中自帶的一個客戶端,放在$ZK_HOME/bin目錄下,本文ZK安裝在/opt/zookeeper-3.4.9

zkCli.sh客戶端連線到ZK伺服器的語法為:zkCli.sh -timeout 5000 -r -server ip:port
連線引數解釋:
1> -timeout:表示客戶端向zk伺服器傳送心跳的時間間隔,單位為毫秒。因為zk客戶端與伺服器的連線狀態是通過心跳檢測來維護的,如果在指定的時間間隔內,zk客戶端沒有向伺服器傳送心跳包,伺服器則會斷開與該客戶端的連線。引數5000,表示zk客戶端向伺服器傳送心跳的間隔為5秒。
2> -r

:表示客戶端以只讀模式連線
3> -server:指定zk伺服器的IP與埠,zk預設的客戶端埠為2181

shell> cd /usr/local/zookeeper/bin
shell> ./zkCli.sh -timeout 5000 -server 127.0.0.1:2181

zkCli.sh連線伺服器成功
若出現上圖提示所示,表示已經成功連線到伺服器。

在客戶端互動命令列中,輸入h查詢可以使用的客戶端命令:

[zk: 127.0.0.1:2181(CONNECTED) 0] h
ZooKeeper -server host:port cmd args
    stat path [watch]
    set
path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port

這些命令的作用和關係型資料庫的SQL語句類似,zk的命令是對節點和資料進行增刪改查操作,而SQL則是對錶的資料增冊改查操作。下面詳細介紹所有命令的使用方法:

1、查詢子節點列表

語法:ls path
path:節點路徑

shell> ls /
[zookeeper]

目前根節點下只有zookeeper一個節點,是zk預設建立的,用於儲存節點的一些狀態資訊,比如節點配額。

2、建立節點

語法:create path [-s] [-e] data acl
path:節點路徑
-s:指定該節點是一個序列節點,建立同名的節點時,會給節點自動加上編號
-e:指定該節點是一個臨時節點,預設是永久節點。臨時節點會在客戶端與伺服器斷開連線時,zk會將其建立的所有臨時節點全部刪除
data:儲存在節點中的資料
acl:設定子節點訪問許可權,預設所有人都可以對該節點進行讀寫操作

# 1> 在根目錄建立了一個`node_01`的節點,指定的資料為mydata
shell> create /node_01 mydata
Created /node_01
shell> ls /
[node_01, zookeeper]

# 2> 建立一個臨時節點(建立之後,可退出客戶端重新登入檢視該節點是否存在,來驗證臨時節點是否被刪除)
shell> create -e /node_02 "i is a ephemeral node"
Created /node_02

# 3> 建立一個序列臨時節點
shell> create -s -e /node_03 'i is a ephemeral sequence node'
Created /node_03

# 4> 建立一個永久序列節點(節點會自動加上編號)
shell> create -s /node_04 data
Created /node_040000000012
shell> create -s /node_04 data
Created /node_040000000013
shell> create -s /node_04 data
Created /node_040000000014

# 5> 建立一個帶許可權的節點,限制只能IP為192.168.1.101這臺機器訪問
## c:建立子節點許可權  
## d:刪除子節點許可權
## r:讀取子節點列表的許可權
## w:寫許可權,即修改子節點資料許可權
## a:管理子節點許可權
shell> create /node_04 mydata ip:192.168.1.101:cdrwa

注意:建立節點必須要為節點設定資料,否則會建立不成功。

3、獲取節點狀態

每個節點都包含描述該節點的一些狀態資訊,比如:節點資料、版本號等。
語法:stat path [watch]
path:節點全路徑
watch:監聽節點狀態變化

shell> stat /node_01 
cZxid = 0x2f
ctime = Sat Nov 12 15:54:05 CST 2016
mZxid = 0x2f
mtime = Sat Nov 12 15:54:05 CST 2016
pZxid = 0x2f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

在ZK中,ZK客戶端對伺服器每一個數據節點的寫操作,ZK會認為都是一次完整的事務操作,要麼成功,要麼失敗,保證了資料的原子性。而每次事務都會分配一個唯一的事務id,以標識這次事務操作的資料資訊。下面詳細理解一下節點狀態各個欄位的含義:
cZxid:建立節點的事務id
ctime:建立節點的時間
mZxid:修改節點的事務id
mtime:修改節點的時間
pZxid:子節點列表最後一次修改的事務id。刪除或新增子節點,不包含修改子節點的資料。
cversion:子節點的版本號,刪除或新增子節點,版本號會自增
dataVersion:節點資料版本號,資料寫入操作,版本號會遞增
aclVersion:節點ACL許可權版本,許可權寫入操作,版本號會遞增
ephemeralOwner:臨時節點建立時的事務id,如果節點是永久節點,則它的值為0
dataLength:節點資料長度(單位:byte),中文佔3個byte
numChildren:子節點數量

4、獲取節點資料

語法:get path [watch]
path:節點路徑
watch:監聽節點資料變化。如果其它客戶端修改了該節點的資料,則會通知監聽了該節點的所有客戶端

shell> get /node_01
mydata
cZxid = 0x2f
ctime = Sat Nov 12 15:54:05 CST 2016
mZxid = 0x2f
mtime = Sat Nov 12 15:54:05 CST 2016
pZxid = 0x2f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

/node_01的節點資料為mydata,即節點狀態資訊的第一行

5、設定節點資料

語法:set path data [version]
path:節點路徑
data:節點資料
version:資料版本號(節點狀態dataVersion的值)

shell> set /node_01 hello
cZxid = 0x2f
ctime = Sat Nov 12 15:54:05 CST 2016
mZxid = 0x30
mtime = Sat Nov 12 15:55:01 CST 2016
pZxid = 0x2f
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

此時可以看到dataVersion狀態的值變成了1。預設不加版本號則會覆蓋節點之前設定的資料,如果加上版本號,版本號必須和伺服器上的版本號一致,否則會報錯,如下所示:

shell> set /node_01 updatedata 2
version No is not valid : /node_01

這種機制和資料庫中的樂觀索機制非常相似。

想象一種場景,在獲取某個節點的資料之後,利用資料處理完業務邏輯,不加版本號,直接修改節點的資料。但在獲取和修改節點資料的這一小段時間窗內,很有可能有其它客戶端也修改了該節點的資料,而節點資料變化會使節點狀態的dataVersion值遞增。如果我們獲取節點資料處理完成自己的業務邏輯,然後不加上版本號直接修改節點資料時,則會覆蓋掉其它客戶端修改的最新資料,從而導致資料不一致的情況。所以要保證資料的一致性時,修改節點資料時,應該加上最新的版本號。而在這個場景中,我們在處理完業務邏輯,再修改節點資料時帶上節點的版本號,這時若有其它節點修改了資料,修改則會失敗。此時我們應該馬上再獲取一次節點的最新版本號,再做修改。

6、查詢子節點列表及狀態資訊

語法:ls2 path [watch]
path:節點路徑
watch:是否監聽子節點列表變化通知

# 先在/node_1節點下建立幾個子節點
shell> create /node_01/node_01_01 abc
Created /node_01/node_01_01
shell> create /node_01/node_01_02 def
Created /node_01/node_01_02
shell> ls2 /node_01
[node_01_01, node_01_02]
cZxid = 0x2f
ctime = Sat Nov 12 15:54:05 CST 2016
mZxid = 0x30
mtime = Sat Nov 12 15:55:01 CST 2016
pZxid = 0x39
cversion = 2
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 2

第一行是/node_01的子節點列表,後面的資訊是/node_01節點的狀態資訊。和ls命令不一樣的是,ls2不僅能查詢節點的子節點列表,同時也能查詢到節點的狀態資訊。

7、刪除節點

語法:delete path [version]
path:節點路徑
version:節點版本號(節點狀態cversion的值),可選。如果傳遞了版本號,則必須保證和伺服器的版本號一致,否則會報錯:version No is not valid : 節點路徑

shell> delete /path/node_01/node_01_01

注意:delete只能刪除沒有子節點的節點,否則會報錯,如下所示:

shell> delete /node_01
Node not empty: /node_01

8、刪除節點(包括子節點)

語法:rmr path
path:節點路徑

shell> rmr /node_01

rmr會遞迴刪除子節點,再刪除節點本身

9、設定節點配額

節點可以儲存資料,也可以建立子節點,但是如果不做控制,節點資料可以無限大,子節點數量也可以建立無數個,所以在有些場景下需要對節點的資料和子節點的數量需要做一些限制,zk為我們提供了setauota命令實現對子節點的限制功能。但是,zk並不是真正在的物理上對節點做了限制,而是如果超過了節點限制,會在zk的日誌檔案中記錄配額超限的警告資訊。

語法:setquota -n|-b val path
-n:限制子節點的數量
-b:限制節點的資料長度
val:根據-n和-b引數不同,val值的意義也不一樣。如果是-n引數,val表示限制子節點的數量。如果是-b引數,val表示限制節點的資料長度
path:節點路徑

shell> setquota -n 2 /node_01
Comment: the parts are option -n val 2 path /node_01

上面我對/node_01限制它最多隻能有2個子節點,下面我在/node_01節點下建立3個節點看看效果:

shell> create /node_01/node_01_01 abc
Created /node_01/node_01_01
shell> create /node_01/node_01_02 abc
Created /node_01/node_01_02
shell> create /node_01/node_01_03 abc
Created /node_01/node_01_03

你可能會覺得奇怪,我明明限制了/node_01節點最多隻能有2個節點,在建立第3個節點的時候並沒有報錯,也建立成功了,為什麼限制沒有起作用呢? 在上面我也提到了,zk並沒有在物理上限制節點的數量和資料的長度,當節點超過了限制,zk只會在後臺記錄節點限制的日誌資訊。下面我們看下zk日誌檔案中輸出的節點配額限制警告資訊:

shell> tail /var/log/zookeeper/zookeeper.log
...
2016-11-12 17:29:20,196 [myid:] - WARN  [SyncThread:0:[email protected]301] - Quota exceeded: /node_01 count=3 limit=2

日誌中輸出的警告資訊count=3,表示/node_01節點當前有3個子節點。limit=2,表示/node_01節點最多隻能有2個節點。

zk預設會在啟動服務的目錄生成一個zookeeper.out日誌檔案,即執行zkServer.sh start命令的目錄。我修改了zk預設日誌檔案目錄為/var/log/zookeeper,你也可以參考《分散式服務管理框架-Zookeeper日誌配置》自行修改。

10、查詢節點配額

語法:listquota path
path:節點路徑

shell> listquota /node_01
absolute path is /zookeeper/quota/node_01/zookeeper_limits
Output quota for /node_01 count=2,bytes=-1
Output stat for /node_01 count=4,bytes=12

Output quota:表示節點的配額資訊,限制該節點最多有2個子節點,節點資料為-1,表示不限制
Output stat:表示當前節點的狀態資訊,該節點有4個子節點,節點資料長度為12

11、刪除節點配額

語法:delquota [-n|-b] path
-n:刪除子節點數量配額限制
-b:刪除節點資料長度配額限制
path:節點路徑

# 刪除/node_01節點子節點數量限制
shell> delquota -n /node_01
shell> listquota /node_01
absolute path is /zookeeper/quota/node_01/zookeeper_limits
# count=-1表示沒有子節點數量限制
Output quota for /node_01 count=-1,bytes=-1
Output stat for /node_01 count=4,bytes=12

12、獲取節點ACL

ACL是zk對節點許可權控制的一種策略
語法:getAcl path
path:節點路徑

shell> getAcl /node_01
'world,'anyone
: cdrwa

建立節點時如果沒有設定acl許可權,預設為所有使用者都可以對該節點進行讀寫操作。

13、設定節點ACL

語法:setAcl path acl
path:節點路徑
acl:ACL許可權模式

shell> setAcl /node_01 ip:192.168.1.101:rcdwa
cZxid = 0x65
ctime = Sat Nov 12 17:29:06 CST 2016
mZxid = 0x65
mtime = Sat Nov 12 17:29:06 CST 2016
pZxid = 0x68
cversion = 3
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 3

設定/node_01節點,只允許IP為192.168.1.101的客戶端訪讀寫/node_01的資料,但不允許建立、查詢、刪除子節點和不允許設定節點許可權。節點ACL詳細介紹請參考《分散式服務管理框架-Zookeeper節點ACL》

14、給當前客戶端新增授權資訊

語法:addauth scheme auth
scheme:授權方式
auth:許可權
addauth一般用於digest授權方式新增授權資訊。digest是使用者名稱和密碼授權,語法:username:BASE64(SHA1(password))

shell> addauth digest yangxin:123456

給當前客戶端新增授權資訊,授權模式為digest(使用者名稱/密碼授權),使用者名稱為yangxin,密碼為123456

15、檢視歷史命令

可查詢之前執行過的命令,會列出前最後10條命令,和linux中的history命令功能一樣

shell> history
11 - setAcl /node_04 ip:192.168.1.101:cra
12 - setAcl /node_04 ip:192.168.1.101:craw
13 - create /node_04/node_04_01 aaa
14 - delete /node_04/node_04_01
15 - get /node_04/node_04_01
16 - getAcl /node_04/node_04_01
17 - getAcl /node_04
18 - h
19 - addauth ip rwcda
20 - getAcl /node_04
21 - history

16、執行歷史命令

語法:redo cmdno
cmdno:歷史命令編號

shell>redo 17
'ip,'192.168.1.101
: crwa

和linux中!命令編號命令的作用一樣

17、與leader同步資料

語法:sync path
path:節點路徑

在對某個znode進行讀操作時,應該先執行sync方法,使得讀操作的連線所連的zk例項能與leader進行同步,從而保證能讀到最新的資料。
注意:sync呼叫是非同步的,無需等待呼叫的返回,zk伺服器會保證所有後續的操作會在sync操作完成之後才執行,哪怕這些操作是在執行sync之前被提交的。

shell> sync /node_01

18、開啟或關閉監聽日誌

在獲取節點資料、子節點列表等操作時,都可以新增watch引數監聽節點的變化,從而節點資料更改、子節點列表變更時收到通知,並輸出到控制檯。預設是開啟,可以設定引數將其關閉。
語法:printwatches on|off
on:開啟
off:關閉

shell> printwatches off

19、關閉連線

close命令會關閉當前客戶端連線

20、連線到zk伺服器

語法:connect host:port
host:port:IP和zk客戶端埠

shell>connect 192.168.1.102:2181

21、退出zkCli.sh終端

quit