1. 啟動客戶端
[admin@yrjk bin]$ ./zkCli.sh
[zk: localhost:2181(CONNECTED) 0]
2. 顯示所有操作命令
[zk: localhost:2181(CONNECTED) 0] help 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 [zk: localhost:2181(CONNECTED) 1]
3. 查看節點
[zk: localhost:2181(CONNECTED) 1] ls / # ls命令用於查看節點,類似於Linux中的查看目錄 [zookeeper] [zk: localhost:2181(CONNECTED) 2] 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) 3]
節點狀態的屬性描述如下:

[zk: localhost:2181(CONNECTED) 3] create /name Richard Created /name [zk: localhost:2181(CONNECTED) 4] stat /name # stat命令用於查看節點狀態的詳細信息 cZxid = 0x6 ctime = Sun Dec 15 13:25:02 CST 2019 mZxid = 0x6 mtime = Sun Dec 15 13:25:02 CST 2019 pZxid = 0x6 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 0 [zk: localhost:2181(CONNECTED) 5] get /name # get命令用於查看節點的數據以及節點狀態的詳細信息 Richard cZxid = 0x6 ctime = Sun Dec 15 13:25:02 CST 2019 mZxid = 0x6 mtime = Sun Dec 15 13:25:02 CST 2019 pZxid = 0x6 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 0 [zk: localhost:2181(CONNECTED) 6]
4. 節點創建
znode一共有4種類型:持久的(persistent)、臨時的(ephemeral)、持久有序的(persistent_sequential)和臨時有序的(ephemeral_sequential)。
在節點創建之前,需要了解一下zk中session的特性:
- 客戶端與服務端之間的連接存在會話
- 每個會話都可以設置一個超時時間
- 心跳結束,session則過期
- session過期,則臨時節點znode會被拋棄
- 心跳機制:客戶端向服務端的ping包請求
[zk: localhost:2181(CONNECTED) 2] create /name Richard Created /name [zk: localhost:2181(CONNECTED) 3] ls / [name, zookeeper] [zk: localhost:2181(CONNECTED) 4] get /name Richard cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0xe mtime = Sun Dec 15 13:53:22 CST 2019 pZxid = 0xe cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 0 [zk: localhost:2181(CONNECTED) 5]
這種創建方式創建出來的節點是持久化的,也就是持久節點(PERSISTENT)。所謂持久節點,是指在節點創建后,就一直存在,直到有刪除操作來主動清除這個節點——不會因為創建該節點的客戶端會話失效而消失。除了持久節點之外,我們還可以創建臨時節點(EPHEMERAL),那么我們來看看如何創建臨時節點:
[zk: localhost:2181(CONNECTED) 5] create -e /name/tmp Rachel Created /name/tmp # -e 指定創建臨時節點 [zk: localhost:2181(CONNECTED) 6] get /name Richard cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0xe mtime = Sun Dec 15 13:53:22 CST 2019 pZxid = 0xf cversion = 1 #由於在/name下創建了一個子節點,所以 cversion 的值就會進行累加 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 #表示持久節點 dataLength = 7 numChildren = 1 [zk: localhost:2181(CONNECTED) 7] get /name/tmp Rachel cZxid = 0xf ctime = Sun Dec 15 13:56:09 CST 2019 mZxid = 0xf mtime = Sun Dec 15 13:56:09 CST 2019 pZxid = 0xf cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x100000be9f80004 #表示臨時節點 dataLength = 6 numChildren = 0 [zk: localhost:2181(CONNECTED) 8]
和持久節點不同的是,臨時節點的生命周期和客戶端會話綁定。也就是說,如果客戶端會話失效,那么這個節點就會自動被清除掉。注意,這里提到的是會話失效,而非連接斷開,當然連接斷開也會導致會話失效,但是並不是主要原因。另外,在臨時節點下面不能創建子節點。我們上面提到了,心跳結束,session就會過期,而session過期,則臨時節點znode就會被拋棄。那么我們來斷開與服務端的連接,看看臨時節點是否會被清除:
[zk: localhost:2181(CONNECTED) 8] ls /name [tmp] [zk: localhost:2181(CONNECTED) 9] quit Quitting... 2019-12-15 14:00:44,326 [myid:] - INFO [main:ZooKeeper@687] - Session: 0x100000be9f80004 closed 2019-12-15 14:00:44,328 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x100000be9f80004 [admin@yrjk bin]$ ./zkCli.sh [zk: localhost:2181(CONNECTED) 0] ls /name #重連之后,臨時節點被清除 [] [zk: localhost:2181(CONNECTED) 1]
以上我們演示了持久節點和臨時節點的創建,下面我們來看一下持久順序節點(PERSISTENT_SEQUENTIAL)的創建:
[zk: localhost:2181(CONNECTED) 3] create -s /name/seq num #-s指定創建持久順序節點 Created /name/seq0000000001 # 會自動為給定節點名加上一個數字后綴 [zk: localhost:2181(CONNECTED) 4] ls /name [seq0000000001] [zk: localhost:2181(CONNECTED) 5] create -s /name/seq num Created /name/seq0000000002 # 再次創建節點數字就會遞增 [zk: localhost:2181(CONNECTED) 6] ls /name [seq0000000001, seq0000000002] [zk: localhost:2181(CONNECTED) 7] create -s /name/otherseq num Created /name/otherseq0000000003 # 創建前綴不同的節點,數字也會遞增 [zk: localhost:2181(CONNECTED) 8] ls /name [otherseq0000000003, seq0000000001, seq0000000002] [zk: localhost:2181(CONNECTED) 9]
這類節點的基本特性和持久節點類型是一致的。額外的特性是,在ZK中,每個父節點會為他的第一級子節點維護一份時序,會記錄每個子節點創建的先后順序。基於這個特性,在創建子節點的時候,可以設置這個屬性,那么在創建節點過程中,ZK會自動為給定節點名加上一個數字后綴,作為新的節點名。這個數字后綴的范圍是整型的最大值。
當 -s 與 -e 選項同時使用就是創建臨時順序節點(EPHEMERAL_SEQUENTIAL) ,此節點是屬於臨時節點,不過帶有順序,和臨時節點一樣,當session過期節點就會消失,而客戶端會話連接結束也會導致session過期,所以同樣的該節點也會消失,這種類型的節點一般用於實現分布式鎖。以下演示一下臨時順序節點的創建方式:
[zk: localhost:2181(CONNECTED) 9] create /address Beijing #創建一個持久節點 Created /address [zk: localhost:2181(CONNECTED) 10] create -s -e /address/road Changan #在持久節點下,創建臨時順序節點 Created /address/road0000000000 [zk: localhost:2181(CONNECTED) 11] create -s -e /address/road Changan Created /address/road0000000001 [zk: localhost:2181(CONNECTED) 12] create -s -e /address/street Changan Created /address/street0000000002 [zk: localhost:2181(CONNECTED) 13] ls /address [road0000000000, road0000000001, street0000000002] [zk: localhost:2181(CONNECTED) 14] quit Quitting... 2019-12-15 14:15:09,721 [myid:] - INFO [main:ZooKeeper@687] - Session: 0x100000be9f80005 closed 2019-12-15 14:15:09,723 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x100000be9f80005 [admin@yrjk bin]$ ./zkCli.sh [zk: localhost:2181(CONNECTED) 0] ls /address []
5. 節點修改
使用set命令對某個節點進行修改:
[zk: localhost:2181(CONNECTED) 6] get /name Richard cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0xe mtime = Sun Dec 15 13:53:22 CST 2019 pZxid = 0x14 cversion = 5 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 3 [zk: localhost:2181(CONNECTED) 7] set /name David #修改/name節點的數據 cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0x1b mtime = Sun Dec 15 14:19:21 CST 2019 pZxid = 0x14 cversion = 5 dataVersion = 1 # 此時數據版本就會遞增為1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 5 numChildren = 3 [zk: localhost:2181(CONNECTED) 8] get /name David cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0x1b mtime = Sun Dec 15 14:19:21 CST 2019 pZxid = 0x14 cversion = 5 dataVersion = 1 # 數據版本遞增為1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 5 numChildren = 3 [zk: localhost:2181(CONNECTED) 9]
我們可以利用數據版本 dataVersion 來達到一個樂觀鎖的效果,所以每次我們修改節點數據的時候,應該加上這個 dataVersion 的值去進行修改,以免在並發的時候導致數據不一致:
[zk: localhost:2181(CONNECTED) 9] set /name Sam 1 #指定版本去修改 cZxid = 0xe ctime = Sun Dec 15 13:53:22 CST 2019 mZxid = 0x1c mtime = Sun Dec 15 14:22:33 CST 2019 pZxid = 0x14 cversion = 5 dataVersion = 2 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 3 numChildren = 3 [zk: localhost:2181(CONNECTED) 10] set /name Sam1 1 # 如果再次使用版本1去修改數據,就會報錯 version No is not valid : /name [zk: localhost:2181(CONNECTED) 11]
6. 節點刪除
使用delete命令可以對某個節點進行刪除:
[zk: localhost:2181(CONNECTED) 12] ls /name [otherseq0000000003, seq0000000001, seq0000000002] [zk: localhost:2181(CONNECTED) 13] delete /name/seq0000000001 #刪除節點 [zk: localhost:2181(CONNECTED) 14] ls /name [otherseq0000000003, seq0000000002] [zk: localhost:2181(CONNECTED) 15] delete /name/seq0000000002 0 #指定版本號刪除節點 [zk: localhost:2181(CONNECTED) 16] ls /name [otherseq0000000003]
使用rmr命令可以對節點進行遞歸刪除:
[zk: localhost:2181(CONNECTED) 16] ls / [name, address, zookeeper] [zk: localhost:2181(CONNECTED) 17] rmr /name #遞歸刪除節點 [zk: localhost:2181(CONNECTED) 18] ls / [address, zookeeper] [zk: localhost:2181(CONNECTED) 19]
7. watcher機制
watcher是zk中比較重要的特性,定義如下:
- 針對每個節點的操作,都會有一個監督者 -> watcher
- 當監控的某個對象(znode)發生了變化,則觸發watcher事件
- 簡單來說,watcher類似於sql中的觸發器
- zk中的watcher是一次性的,觸發后立即銷毀
- 父節點,子節點 的增刪改都能夠觸發其watcher
- 針對不同類型的操作,觸發的watcher事件也不同:
- (子)節點創建事件
- (子)節點刪除事件
- (子)節點數據變化事件
7.1 父節點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]
7.2 子節點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:分布式過程協同技術詳解
