ZooKeeper CLI(客户端)
ZooKeeper命令行界面(CLI)用于与ZooKeeper服务端进行交互,以进行开发。它有助于调试和解决不同的选项。
要执行ZooKeeper CLI操作,首先打开ZooKeeper服务器。
进入ZooKeeper的bin目录执行以下命令
./zkServer.sh start
启动成功
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
然后打开ZooKeeper客户端
./zkCli.sh
启动成功
Connecting to localhost:2181
2019-08-24 17:21:13,419 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.9-1757313, built on 08/23/2016 06:50 GMT
2019-08-24 17:21:13,422 [myid:] - INFO [main:Environment@100] - Client environment:host.name=bogon
2019-08-24 17:21:13,422 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_181
2019-08-24 17:21:13,424 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation
2019-08-24 17:21:13,424 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-7.b13.el7.x86_64/jre
2019-08-24 17:21:13,424 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/opt/zookeeper-3.4.9/bin/../build/classes:/opt/zookeeper-3.4.9/bin/../build/lib/*.jar:/opt/zookeeper-3.4.9/bin/../lib/slf4j-log4j12-1.6.1.jar:/opt/zookeeper-3.4.9/bin/../lib/slf4j-api-1.6.1.jar:/opt/zookeeper-3.4.9/bin/../lib/netty-3.10.5.Final.jar:/opt/zookeeper-3.4.9/bin/../lib/log4j-1.2.16.jar:/opt/zookeeper-3.4.9/bin/../lib/jline-0.9.94.jar:/opt/zookeeper-3.4.9/bin/../zookeeper-3.4.9.jar:/opt/zookeeper-3.4.9/bin/../src/java/lib/*.jar:/opt/zookeeper-3.4.9/bin/../conf:
2019-08-24 17:21:13,424 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2019-08-24 17:21:13,424 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA>
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:os.version=3.10.0-957.el7.x86_64
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:user.name=root
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/root
2019-08-24 17:21:13,425 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/opt/zookeeper-3.4.9/bin
2019-08-24 17:21:13,426 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@7aec35a
2019-08-24 17:21:13,460 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error)
Welcome to ZooKeeper!
JLine support is enabled
2019-08-24 17:21:13,607 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@876] - Socket connection established to localhost/0:0:0:0:0:0:0:1:2181, initiating session
2019-08-24 17:21:13,639 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x16cc029862a0005, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
一旦ZooKeeper客户端启动成功,我们就可以和ZooKeeper服务端进行交互。
输入help
命令(其实输入任何 zkCli 不能识别的命令,都会列出所有的命令),查看可用的命令:
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
现在让我们用一个例子逐个了解上面的命令
创建节点 create
语法
create [-s] [-e] path data acl
注意:如果该节点不需要存储任何数据则用''
字符串表示,不可忽略,否则会报错。
-s
创建有序节点
顺序节点可以是持久的或临时的。当一个新的节点被创建为一个顺序节点时,ZooKeeper通过将10位的序列号附加到原始名称来设置节点的路径。
例如,如果将具有路径 /myapp 的节点创建为顺序节点,则ZooKeeper会将路径更改为/myapp0000000001 ,并将下一个序列号设置为0000000002。如果两个顺序节点是同时创建的,那么ZooKeeper不会对每个节点使用相同的数字。顺序节点在锁定和同步中起重要作用。
这个序号的计数器是由这些排序节点的父节点来维护的。
-e
创建临时节点
客户端活跃时,临时节点就是有效的。当客户端与ZooKeeper集合断开连接时,临时节点会自动删除。因此,只有临时节点不允许有子节点。如果临时节点被删除,则下一个合适的节点将填充其位置。临时节点在leader选举中起着重要作用。
acl
我们单独在ZooKeeper之ACL做介绍。
使用方法:
创建普通节点
create /mynode monkey
结果:
Created /mynode
创建子节点
在/mynode
节点下添加一个名为/subnode
的子节点
create /mynode/subnode ''
结果:
Created /mynode/subnode
如果父节点不存在,则会先创建父节点后再创建子节点。
注意:临时节点不能拥有子节点。
创建有序节点
create -s /mynode ''
create -s /mynode ''
结果:
Created /mynode0000000003
Created /mynode0000000004
创建临时节点
create -e /temp ''
结果:
Created /temp
关闭当前会话,然后重新打开它,发现/tmep
节点已经被删除了。
创建临时有序节点
create -s -e /mynode ''
结果:
Created /mynode0000000006
关闭当前会话,然后重新打开它,发现/mynode0000000006
节点已经被删除了。
列出znode的子节点 ls or ls2
语法
ls path [watch]
ls
命令类似于Linux中的dir
ls2 path [watch]
ls2
命令来查看当前节点的所有子节点并能看到更新次数等数据,与ls
不同的是它可以当前节点的stat(状态)。
watch
添加一个监视器
[watch]
添加一个watch
(监视器)如果该节点发生变化,watch
可以使客户端得到通知。watch 只能被触发一次。如果要一直获得znode的创建和删除的通知,那么就需要不断的在znode上开启观察模式。
如果在该 path 下创建或删除子节点,会产生 NodeChildrenChanged
事件;如果删除自身节点则会产生 NodeDeleted
事件。
使用方法:
ls
列出根节点下的所有子节点
ls /
结果:
[mynode0000000006, mynode, zookeeper, mynode0000000003]
ls2
列出根节点
ls2 /
结果:
[mynode0000000006, mynode, zookeeper, mynode0000000003]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 HKT 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 HKT 1970
pZxid = 0x2c
cversion = 10
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 4
列出子节点
ls /mynode
结果:
[subnode]
使用watch
我们在根节点创建一个名为a
的watch
ls / a
然后再根节点下添加子节点
create /mynode2 hello
就会触发该watch
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
Created /mynode2
注意:在其他节点下创建子节点,不会触发该watch
。
从上面的操作可以看到,在根节点添加了 /mynode2
节点之后,触发了 watch
,WatchedEvent
的类型是 NodeChildrenChanged
。
获取节点信息 get
语法
get path [watch]
watch
添加一个监视器
[watch]
添加一个watch
(监视器)如果该节点发生变化,watch
可以使客户端得到通知。watch 只能被触发一次。如果要一直获得znode的创建和删除的通知,那么就需要不断的在znode上开启观察模式。
如果节点内容发生改变,会产生NodeDataChanged
事件;如果删除节点,会产生NodeDeleted
事件。
获取/mynode
数据
get /mynode
结果
22
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x3e
mtime = Sun Aug 25 14:27:54 HKT 2019
pZxid = 0x3d
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
每一个对znode树的更新操作,都会被赋予一个全局唯一的ID,我们称之为zxid
(Zookeeper Transaction ID)。更新操作的ID按照发生的时间顺序升序排序。
Znode(节点)的stat结构中的字段显示如下,各自的含义如下:
- cZxid - 创建该
znode
的zxid
。 - ctime - 创建该
znode
的时间。 - mZxid - 这是最后修改
znode
更改的事务ID。 - mtime - 最后一次修改该
znode
的时间。 - pZxid - 这是用于添加或删除子节点的
znode
更改的事务ID。 - cversion - 该
znode
的子节点的版本。 - dataVersion -
znode
内容的版本,每次修改内容,版本都会增加。 - aclVersion -
znode
的ACL
的版本。 - ephemeralOwner - 如果
znode
是ephemeral(临时)节点,会列出该znode
所在客户端的session id
;如果不是临时节点,该值为0
。 - dataLength - 该
znode
存储的数据长度。 - numChildren - 该
znode
的子节点的个数。
使用watch
我们在/mynode
节点创建一个名为a
的watch
get /mynode a
然后更改/mynode
节点的数据
set /mynode 111
就会触发watch
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/mynode
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x40
mtime = Sun Aug 25 17:08:53 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
从上面的操作可以看到,在修改/mynode
的数据后,触发了 watch
,WatchedEvent
的类型是 NodeDataChanged
。
检查状态 stat
语法
stat path [watch]
watch
添加一个监视器
[watch]
添加一个watch
(监视器)如果该节点发生变化,watch
可以使客户端得到通知。watch 只能被触发一次。如果要一直获得znode的创建和删除的通知,那么就需要不断的在znode上开启观察模式。
如果节点内容发生改变,会产生NodeDataChanged
事件;如果删除节点,会产生NodeDeleted
事件。
使用方法:
查看/mynode
状态
stat /mynode
结果:
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x40
mtime = Sun Aug 25 17:08:53 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
与get
的区别是,不会列出节点的数据。
使用watch
我们在/mynode
节点创建一个名为a
的watch
stat /mynode a
然后更改/mynode
节点的数据
set /mynode 222
就会触发watch
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/mynode
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x41
mtime = Sun Aug 25 22:22:24 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
从上面的操作可以看到,在修改/mynode
的数据后,触发了 watch
,WatchedEvent
的类型是 NodeDataChanged
。
设置数据 set
语法
set path data [version]
修改已经存在的节点的数据。
version
指定版本号设置数据
[version]
参数用于指定节点的数据版本,表名本次更新操作是针对指定的数据版本进行更新的。
如果指定znode版本号,需要与当前的版本号匹配。如果版本号不匹配,操作将会失败。失败的原因可能是在我们提交之前,该znode已经被修改过了,版本号发生了增量变化。如果不指定版本号,就是直接操作最新版本的 znode。
使用方法
修改/mynode
的数据为sayhello
set /mynode sayhello
结果:
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x47
mtime = Sun Aug 25 23:18:27 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 5
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 1
我们通过get
命令来查看/mynode
sayhello
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x47
mtime = Sun Aug 25 23:18:27 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 5
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 1
可以看到sayhello
已经被设置进去了。
我们来看看带version
参数的set
通过stat
命令我们可以看到mynode
的dataVersion
为11
修改/mynode
的数据为sayhi
,指定版本参数为11
set /mynode sayhi 11
结果:
cZxid = 0x3d
ctime = Sun Aug 25 14:27:31 HKT 2019
mZxid = 0x5d
mtime = Mon Aug 26 11:19:56 HKT 2019
pZxid = 0x3f
cversion = 1
dataVersion = 12
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 1
可以看到,在修改节点值之后,mZxid
、mtime
、dataVersion
都发生了变化。
注意:如果version
参数和/mynode
节点的dataVersion
所对应不上则会提示version No is not valid :/mynode
(版本号无效:/mynode),并且修改数据失败。
指定版本更新的意义何在?
通俗来讲就是CAS
(乐观锁),每次更新携带上次获取到的vesion
值进行更新。而如果在这段时间内,ZooKeeper服务器上该节点的数值恰好已经被其它客户端更新了,那么其节点的version
值也一定会发生变化,一旦节点的version
值发生了变化。我们的本次携带的vesion
值就无法和服务端节点的vesion
值匹配,于是便无法成功更新。 – 因此可以有效地避免一些分布式更新的并发问题
删除节点 rmr
语法
rmr path
使用方法:
删除/mynode
节点
rmr /mynode
使rmr
命令删除,不会返回任何内容。如果有子节点的时候,连带子节点也一起删除。
我们通过ls
命令来查看/mynode
节点是否被删除了。
ls /
结果:
[zookeeper, mynode0000000003]
可以看到/mynode
节点已经被删除了。
删除节点 delete
语法
delete path [version]
version
指定版本号删除节点
[version]
参数用于指定节点的数据版本,表名本次更新操作是针对指定的数据版本进行更新的。如果指定znode版本号,需要与当前的版本号匹配。如果版本号不匹配,操作将会失败。失败的原因可能是在我们提交之前,该znode已经被修改过了,版本号发生了增量变化。如果不指定版本号,就是直接操作最新版本的 znode。
使用方法:
我们先使用create
创建一个名为/mynode
节点
create /mynode ''
结果:
Created /mynode
接着我们来使用delete
命令删除/mynode
节点。
delete /mynode
使delete
命令删除,不会返回任何内容。如果有子节点的时候,将不能删除。
我们通过ls
命令来查看/mynode
节点是否被删除了。
ls /
结果:
[zookeeper, mynode0000000003]
可以看到/mynode
节点已经被删除了。
我们来看看带version
参数的delete
我们先使用create
创建一个名为/mynode
节点
create /mynode ''
结果:
Created /mynode
通过stat
命令我们可以看到mynode
的dataVersion
为0
删除/mynode
,指定版本参数为0
delete /mynode 0
我们通过ls
命令来查看/mynode
节点是否被删除了。
ls /
结果:
[zookeeper, mynode0000000003]
可以看到/mynode
节点已经被删除了。
注意:如果version
参数和/mynode
节点的dataVersion
所对应不上则会提示version No is not valid :/mynode
(版本号无效:/mynode),并且删除失败。
delete
和rmr
的区别
delete
只能删除叶节点,也就是说带有子节点的节点将删除失败,相反rmr
可以删除带有子节点的节点。delete
能带version
进行删除,相反rmr
不支持
其他指令
历史记录 history
语法
history
使用方法:
列出最近的10条历史记录。
history
结果:
14 - rmr /mynode
15 - ls /
16 - create /mynode ''
17 - delete /mynode
18 - ls /
19 - create /mynode ''
20 - stat /mynode
21 - delete /mynode 1
22 - delete /mynode 0
23 - help
24 - history
重复之前的命令 redo
语法
redo cmdno
使用方法:
根据 cmdno 重复之前的命令,cmdno 就是方括号里面最后的数字,每次执行命令都会自增
[zk: localhost:2181(CONNECTED) 28] create /mynode ''
Created /mynode
[zk: localhost:2181(CONNECTED) 29] rmr /mynode
[zk: localhost:2181(CONNECTED) 30] redo 28
Created /mynode
是否输出 watch 事件 printwatches on | off
语法
printwatches on|off
on
代表会输出watch
事件,off
代表不会输出watch
事件
使用方法:
查看当前printwatches
的值
printwatches
结果:
printwatches is on
我们在根节点创建一个名为a
的watch
ls / a
并删除掉根节点中的其中一个子节点,触发watch
rmr /mynode
结果:
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
输出了watch
事件
我们将printwatches
的值更改为off
printwatches off
我们继续在根节点创建一个名为a
的watch
ls / a
并在根节点中创建一个子节点,触发watch
create /mynode ''
结果:
Created /mynode
并没有输出watch
事件.
关闭连接 close
语法
close
使用方法:
关闭连接(会话)
close
结果:
2019-08-26 18:07:57,745 [myid:] - INFO [main:ZooKeeper@684] - Session: 0x16cc029862a000d closed
[zk: localhost:2181(CLOSED) 39] 2019-08-26 18:07:57,748 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@519] - EventThread shut down for session: 0x16cc029862a000d
再次对节点操作时
ls /
结果:
Not connected
打开连接 connect
语法
connect host:port
指定 host:port 可以连接远程的 zk 服务。缺省的时候,会连接本地的 2181 端口。
使用方法:
连接本地的ZooKeeper服务端
connect localhost:2181
结果:
2019-08-26 18:15:32,037 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@73a8dfcc
[zk: localhost:2181(CONNECTING) 4] 2019-08-26 18:15:32,041 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-08-26 18:15:32,043 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@876] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-08-26 18:15:32,055 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x16cc029862a000f, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
再次对节点操作时
ls /
结果:
[mynode, zookeeper, mynode0000000003]
连接成功!!!
如果是连接本地ZooKeeper服务端并端口号是2181
。则可以省略host:port
参数。
退出客户端 quit
语法
quit
直接退出当前的 zkCli 命令行。
使用方法:
退出客户端
[zk: localhost:2181(CONNECTED) 6] quit
结果:
[root@bogon bin]#
强制同步 sync
语法
sync path
sync
方法会强制客户端所连接的服务器状态与leader的状态同步,这样在读取节点的值就是最新的值了。
使用方法:
同步/mynode
节点
sync /mynode
结果:
Sync returned 0
由于我这边是单机架构所以returned
为 0