1. 程式人生 > >zookeeper基本特性與基於Linux的ZK客戶端命令行學習

zookeeper基本特性與基於Linux的ZK客戶端命令行學習

zookeeper 命令行 客戶端 watcher 事件

zookeeper常用命令行操作
  1. 通過 zkCli.sh 來打開zk客戶端:
[root@study-01 ~]# zkCli.sh
[zk: localhost:2181(CONNECTED) 0]
  1. ls 與 ls2 命令:
[zk: localhost:2181(CONNECTED) 0] ls /  # ls命令用於查看節點,類似於Linux中的查看目錄
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls2 /  # ls2命令用於查看節點以及該節點狀態的詳細信息
[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
[zk: localhost:2181(CONNECTED) 2]
  1. get 與 stat命令:
[zk: localhost:2181(CONNECTED) 2] stat /  # stat命令用於查看節點狀態的詳細信息
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
[zk: localhost:2181(CONNECTED) 3] get /  # get命令用於查看節點的數據以及節點狀態的詳細信息,由於沒有數據所以這裏顯示的是空行

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
[zk: localhost:2181(CONNECTED) 4] 

關於節點狀態屬性信息的描述,如下表:

屬性 描述
cZxid 節點的創建時間所對應的Zxid格式時間戳
ctime 節點的創建時間
mZxid 節點最後的修改時間所對應的Zxid格式時間戳
mtime 節點最後的的修改時間
pZxid 該節點的子節點(或該節點)的最近一次 創建 / 刪除 所對應的Zxid格式時間戳
cversion 節點所擁有的子節點被修改的版本號,刪除或添加子節點,版本號會自增
dataVersion 當前節點數據的版本號,數據寫入操作,版本號會遞增
aclVersion 節點ACL權限版本,權限寫入操作,版本號會遞增
ephemeralOwner 臨時節點創建時的事務id,如果節點是永久節點,則它的值為0
dataLength 節點數據長度(單位:byte),中文占3個byte
numChildren 子節點數量

session的基本原理與create命令的使用

zk特性-session的基本原理:

  • 客戶端與服務端之間的連接存在會話
  • 每個會話都可以設置一個超時時間
  • 心跳結束,session則過期
  • session過期,則臨時節點znode會被拋棄
  • 心跳機制:客戶端向服務端的ping包請求

create命令的使用:

[zk: localhost:2181(CONNECTED) 7] create /testDir test-data  # 創建一個節點,節點的數據為test-data
Created /testDir
[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper, testDir]
[zk: localhost:2181(CONNECTED) 9] get /testDir
test-data
cZxid = 0x4
ctime = Sun Apr 22 18:17:56 CST 2018
mZxid = 0x4
mtime = Sun Apr 22 18:17:56 CST 2018
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
[zk: localhost:2181(CONNECTED) 10]

這種創建方式創建出來的節點是持久化的,也就是持久節點(PERSISTENT)。所謂持久節點,是指在節點創建後,就一直存在,直到有刪除操作來主動清除這個節點——不會因為創建該節點的客戶端會話失效而消失。除了持久節點之外,我們還可以創建臨時節點(EPHEMERAL),那麽我們來看看如何創建臨時節點:

[zk: localhost:2181(CONNECTED) 11] create -e /testDir/tmp tmp-data  # -e指定創建的節點是臨時節點
Created /testDir/tmp
[zk: localhost:2181(CONNECTED) 12] get /testDir
test-data
cZxid = 0x4
ctime = Sun Apr 22 18:17:56 CST 2018
mZxid = 0x4
mtime = Sun Apr 22 18:17:56 CST 2018
pZxid = 0x5
cversion = 1  # 由於在testDir下創建了一個子節點,所以 cversion 的值就會進行累加
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0  # 表示持久節點
dataLength = 9
numChildren = 1
[zk: localhost:2181(CONNECTED) 16] get /testDir/tmp 
tmp-data
cZxid = 0x5
ctime = Sun Apr 22 18:20:26 CST 2018
mZxid = 0x5
mtime = Sun Apr 22 18:20:26 CST 2018
pZxid = 0x5
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x10000052f5b0000  # 表示臨時節點
dataLength = 8
numChildren = 0
[zk: localhost:2181(CONNECTED) 17] 

和持久節點不同的是,臨時節點的生命周期和客戶端會話綁定。也就是說,如果客戶端會話失效,那麽這個節點就會自動被清除掉。註意,這裏提到的是會話失效,而非連接斷開,當然連接斷開也會導致會話失效,但是並不是主要原因。另外,在臨時節點下面不能創建子節點。我們上面提到了,心跳結束,session就會過期,而session過期,則臨時節點znode就會被拋棄。那麽我們來斷開與服務端的連接,看看臨時節點是否會被清除:

[zk: localhost:2181(CONNECTED) 17] quit  # 退出
Quitting...
2018-04-22 18:25:44,884 [myid:] - INFO  [main:ZooKeeper@687] - Session: 0x10000052f5b0000 closed  # session關閉了
2018-04-22 18:25:44,885 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x10000052f5b0000
[root@study-01 ~]# zkCli.sh  # 重新連接服務端
[zk: localhost:2181(CONNECTED) 0] ls /testDir  # 可以看到,tmp節點就消失了
[]
[zk: localhost:2181(CONNECTED) 1]

以上我們演示了持久節點和臨時節點的創建,下面我們來看一下持久順序節點(PERSISTENT_SEQUENTIAL)的創建:

[zk: localhost:2181(CONNECTED) 1] create -s /testDir/sec seq  # -s指定創建持久順序節點
Created /testDir/sec0000000001  # 會自動為給定節點名加上一個數字後綴
[zk: localhost:2181(CONNECTED) 2] ls /testDir
[sec0000000001]
[zk: localhost:2181(CONNECTED) 3] create -s /testDir/sec seq
Created /testDir/sec0000000002  # 再次創建節點數字就會遞增
[zk: localhost:2181(CONNECTED) 4] ls /testDir               
[sec0000000001, sec0000000002]  # 這時就會有兩個節點
[zk: localhost:2181(CONNECTED) 7] create -s /testDir/test seq  # 創建前綴不同的節點,數字也會遞增
Created /testDir/test0000000003
[zk: localhost:2181(CONNECTED) 8] ls /testDir                
[test0000000003, sec0000000001, sec0000000002]
[zk: localhost:2181(CONNECTED) 9] 

這類節點的基本特性和持久節點類型是一致的。額外的特性是,在ZK中,每個父節點會為他的第一級子節點維護一份時序,會記錄每個子節點創建的先後順序。基於這個特性,在創建子節點的時候,可以設置這個屬性,那麽在創建節點過程中,ZK會自動為給定節點名加上一個數字後綴,作為新的節點名。這個數字後綴的範圍是整型的最大值。

當 -s 與 -e 選項同時使用就是創建臨時順序節點(EPHEMERAL_SEQUENTIAL) ,此節點是屬於臨時節點,不過帶有順序,和臨時節點一樣,當session過期節點就會消失,而客戶端會話連接結束也會導致session過期,所以同樣的該節點也會消失,這種類型的節點一般用於實現分布式鎖。以下演示一下臨時順序節點的創建方式:

[zk: localhost:2181(CONNECTED) 15] create /testTmp testTmp-data  # 創建一個持久節點
Created /testTmp
[zk: localhost:2181(CONNECTED) 16] create -s -e /testTmp/secTmp secTmp-data  # 在該節點下,創建臨時順序節點
Created /testTmp/secTmp0000000000
[zk: localhost:2181(CONNECTED) 17] create -s -e /testTmp/secTmp secTmp-data
Created /testTmp/secTmp0000000001
[zk: localhost:2181(CONNECTED) 18] create -s -e /testTmp/testTmp secTmp-data
Created /testTmp/testTmp0000000002
[zk: localhost:2181(CONNECTED) 19] ls /testTmp
[testTmp0000000002, secTmp0000000001, secTmp0000000000]
[zk: localhost:2181(CONNECTED) 20]

斷開客戶端與服務端的連接,看看臨時順序節點是否會被清除:

[zk: localhost:2181(CONNECTED) 21] quit  # 退出
Quitting...
2018-04-22 19:07:13,527 [myid:] - INFO  [main:ZooKeeper@687] - Session: 0x10000052f5b0001 closed
2018-04-22 19:07:13,528 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x10000052f5b0001
[root@study-01 ~]# zkCli.sh
[zk: localhost:2181(CONNECTED) 1] ls /testTmp  # 可以看到,節點都被清除了
[]
[zk: localhost:2181(CONNECTED) 2] 

set與delete命令的使用

使用set命令可以對某個節點進行修改:

[zk: localhost:2181(CONNECTED) 2] set /testDir new-data  # 修改testDir節點的數據
cZxid = 0x4
ctime = Sun Apr 22 18:17:56 CST 2018
mZxid = 0x12
mtime = Sun Apr 22 19:24:41 CST 2018
pZxid = 0xa
cversion = 5
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 3
[zk: localhost:2181(CONNECTED) 3] get /testDir         
new-data   # 可以看到數據更新了
cZxid = 0x4
ctime = Sun Apr 22 18:17:56 CST 2018
mZxid = 0x12
mtime = Sun Apr 22 19:24:41 CST 2018
pZxid = 0xa
cversion = 5
dataVersion = 1  # 此時數據版本就會遞增為1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 3
[zk: localhost:2181(CONNECTED) 4]

我們可以利用數據版本 dataVersion 來達到一個樂觀鎖的效果,所以每次我們修改節點數據的時候,應該加上這個 dataVersion 的值去進行修改,以免在並發的時候導致數據不一致:

[zk: localhost:2181(CONNECTED) 4] set /testDir OneVerstion-data 1  指定版本去修改數據
cZxid = 0x4
ctime = Sun Apr 22 18:17:56 CST 2018
mZxid = 0x13
mtime = Sun Apr 22 19:29:29 CST 2018
pZxid = 0xa
cversion = 5
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 16
numChildren = 3
[zk: localhost:2181(CONNECTED) 5] set /testDir OneVerstion-data 1  # 如果此時別人再次使用版本1去修改數據,就會報錯
version No is not valid : /testDir
[zk: localhost:2181(CONNECTED) 6] 

使用delete命令可以對某個節點進行刪除:

[zk: localhost:2181(CONNECTED) 8] ls /testDir
[test0000000003, sec0000000001, sec0000000002]
[zk: localhost:2181(CONNECTED) 9] delete /testDir/sec0000000001  # 刪除節點
[zk: localhost:2181(CONNECTED) 10] ls /testDir                  
[test0000000003, sec0000000002]
[zk: localhost:2181(CONNECTED) 11] delete /testDir/sec0000000002 0  # 通過指定版本號來刪除節點
[zk: localhost:2181(CONNECTED) 12] ls /testDir                    
[test0000000003]
[zk: localhost:2181(CONNECTED) 13]

watcher機制

watcher是zk中比較重要的特性,定義如下:

  • 針對每個節點的操作,都會有一個監督者 -> watcher
  • 當監控的某個對象(znode)發生了變化,則觸發watcher事件
  • 簡單來說,watcher類似於sql中的觸發器
  • zk中的watcher是一次性的,觸發後立即銷毀
  • 父節點,子節點 的增刪改都能夠觸發其watcher
  • 針對不同類型的操作,觸發的watcher事件也不同:
    • (子)節點創建事件
    • (子)節點刪除事件
    • (子)節點數據變化事件

父節點watcher事件

watcher命令行學習:

  • 通過get path [watcher] 可以設置watcher,其他的諸如stat、ls、ls2命令也可以設置watcher
  • 父節點 增 刪 改 操作觸發watcher
  • 子節點 增 刪 改 操作觸發watcher

watcher事件類型-父節點:

  • 創建父節點觸發 NodeCreated 事件
  • 修改父節點數據觸發 NodeDataChanged 事件
  • 刪除父節點觸發 NodeDeleted 事件

創建父節點觸發 NodeCreated 事件,示例:

[zk: localhost:2181(CONNECTED) 19] stat /testWatch watch  # 在節點創建之前,我們可以通過 stat 命令去設置watcher
Node does not exist: /testWatch
[zk: localhost:2181(CONNECTED) 20] create /testWatch test-data  # 創建父節點

WATCHER::

WatchedEvent state:SyncConnected type:NodeCreated path:/testWatch  # 觸發 NodeCreated 事件
Created /testWatch
[zk: localhost:2181(CONNECTED) 21]

修改父節點數據觸發 NodeDataChanged 事件,示例:

[zk: localhost:2181(CONNECTED) 21] get /testWatch watch  # 因為zk事件是一次性的,所以我們還需要通過 get 命令設置 watcher
test-data
cZxid = 0x19
ctime = Sun Apr 22 23:37:08 CST 2018
mZxid = 0x19
mtime = Sun Apr 22 23:37:08 CST 2018
pZxid = 0x19
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
[zk: localhost:2181(CONNECTED) 22] set /testWatch new-data  # 修改父節點數據

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/testWatch  # 觸發 NodeDataChanged 事件
cZxid = 0x19
ctime = Sun Apr 22 23:37:08 CST 2018
mZxid = 0x1a
mtime = Sun Apr 22 23:40:32 CST 2018
pZxid = 0x19
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0
[zk: localhost:2181(CONNECTED) 23] 

刪除父節點觸發 NodeDeleted 事件,示例:

[zk: localhost:2181(CONNECTED) 23] ls /testWatch watch  # 通過 ls 命令來設置 watcher
[]
[zk: localhost:2181(CONNECTED) 24] delete /testWatch  # 刪除父節點

WATCHER::

WatchedEvent state:SyncConnected type:NodeDeleted path:/testWatch  # 觸發 NodeDeleted 事件
[zk: localhost:2181(CONNECTED) 25]

子節點watcher事件

watcher事件類型-子節點:

  • 使用 ls 命令為父節點設置watcher,創建子節點時就會觸發 NodeChildrenChanged 事件
  • 使用 ls 命令為父節點設置watcher,刪除子節點時也會觸發 NodeChildrenChanged 事件
  • 使用 ls 命令為父節點設置watcher,修改子節點數據時不會觸發任何事件
  • 使用 get 命令為子節點設置watcher,修改子節點數據時會觸發 NodeDataChanged 事件

使用 ls 命令為父節點設置watcher,創建子節點時就會觸發 NodeChildrenChanged 事件,示例:

[zk: localhost:2181(CONNECTED) 29] create /testWatch test-data  # 創建父節點
Created /testWatch
[zk: localhost:2181(CONNECTED) 30] ls /testWatch watch  # 使用 ls 命令為父節點設置watcher
[]
[zk: localhost:2181(CONNECTED) 31] create /testWatch/testChildren children-data  # 創建子節點

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/testWatch  # 觸發 NodeChildrenChanged 事件
Created /testWatch/testChildren
[zk: localhost:2181(CONNECTED) 32] 

使用 ls 命令為父節點設置watcher,刪除子節點時也會觸發 NodeChildrenChanged 事件,示例:

[zk: localhost:2181(CONNECTED) 32] ls /testWatch watch   # 使用 ls 命令為父節點設置watcher
[testChildren]
[zk: localhost:2181(CONNECTED) 33] delete /testWatch/testChildren   # 刪除子節點          

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/testWatch   # 觸發 NodeChildrenChanged 事件
[zk: localhost:2181(CONNECTED) 34] 

簡單說明一下為什麽創建和刪除子節點都是觸發 NodeChildrenChanged 事件,這是因為子節點都是掛在父節點之下,而我們是給父節點設置的 watcher,不是給子節點設置 watcher ,不管子節點是刪除還是創建,都是一個改變的過程,所以都是觸發同一個事件。

使用 ls 命令為父節點設置watcher,修改子節點數據時不會觸發任何事件,示例:

[zk: localhost:2181(CONNECTED) 35] create /testWatch/testChildren children-data  # 創建子節點
[zk: localhost:2181(CONNECTED) 36] ls /testWatch watch   # 使用 ls 命令為父節點設置watcher        
[testChildren]
[zk: localhost:2181(CONNECTED) 37] set /testWatch/testChildren new-children-data  # 修改子節點數據時不會觸發任何事件
cZxid = 0x1f
ctime = Sun Apr 22 23:58:44 CST 2018
mZxid = 0x20
mtime = Sun Apr 22 23:59:24 CST 2018
pZxid = 0x1f
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 17
numChildren = 0
[zk: localhost:2181(CONNECTED) 38] 

不會觸發事件是因為這個watcher是設置在父節點上的,所以修改子節點數據時不會觸發父節點所設置的watcher事件。

使用 get 命令為子節點設置watcher,修改子節點數據時會觸發 NodeDataChanged 事件,示例:

[zk: localhost:2181(CONNECTED) 40] get /testWatch/testChildren watch  # 使用 get 命令為子節點設置watcher
new-children-data
cZxid = 0x1f
ctime = Sun Apr 22 23:58:44 CST 2018
mZxid = 0x21
mtime = Mon Apr 23 00:01:41 CST 2018
pZxid = 0x1f
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 17
numChildren = 0
[zk: localhost:2181(CONNECTED) 41] set /testWatch/testChildren new-children-data2  # 修改子節點數據

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/testWatch/testChildren   # 觸發 NodeDataChanged 事件
cZxid = 0x1f
ctime = Sun Apr 22 23:58:44 CST 2018
mZxid = 0x22
mtime = Mon Apr 23 00:02:11 CST 2018
pZxid = 0x1f
cversion = 0
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 18
numChildren = 0
[zk: localhost:2181(CONNECTED) 42] 

zookeeper基本特性與基於Linux的ZK客戶端命令行學習