目录
一、概述
zookeeper的数据节点可以视为树状结构(或者目录),树中的各节点被称为znode(即zookeeper node),一个znode可以有多个子节点,可以说 zookeeper 中的所有存储的数据是由 znode 组成的,并以 key/value 形式存储数据。整体结构类似于 linux 文件系统的模式以树形结构存储,其中根路径以 / 开头。
[zk: localhost:2181(CONNECTED) 0] ls /
[animal, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /animal
[]
[zk: localhost:2181(CONNECTED) 3] create -e -s /animal/cat "cat"
Created /animal/cat0000000002
[zk: localhost:2181(CONNECTED) 4] ls /animal
[cat0000000002]
可以看到,此时存储的数据在根目录下存在 animal 和 zookeeper 两个节点,animal 节点下存在一个临时有序节点cat,大体结构如下图:
我们还可以使用如下命令获取节点的属性信息:
[zk: localhost:2181(CONNECTED) 6] get /animal
b
cZxid = 0x6
ctime = Mon Dec 14 11:03:43 CST 2020
mZxid = 0x14
mtime = Mon Dec 14 11:16:57 CST 2020
pZxid = 0x1c
cversion = 5
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 1
numChildren = 1
下面对各个属性做一个详细的说明:
- b:数据节点的值;
- cZxid = 0x6:创建数据节点时的事务ID;
- ctime = Mon Dec 14 11:03:43 CST 2020:创建数据节点时的时间;
- mZxid = 0x14:最后一次修改数据节点时的事务ID;
- mtime = Mon Dec 14 11:16:57 CST 2020:最后一次修改数据节点时的时间;
- pZxid = 0x1c:表示该数据节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该ID(注意,只有子节点列表变更了才会变更pzxid,子节点内容变更不会影响pzxid);
- cversion = 5:子节点版本号(子节点的更改次数),子节点每次修改版本号加1;
- dataVersion = 3:数据版本号(节点数据的更改次数),数据每次修改该版本号加1;
- aclVersion = 0:权限版本号(节点的 ACL 的更改次数),权限每次修改该版本号加1;
- ephemeralOwner = 0x0:创建该临时节点的会话的sessionID。(如果该节点是持久节点,那么这个属性值为0);
- dataLength = 1:节点的数据长度;
- numChildren = 1:节点拥有子节点的数量(只统计直接子节点的数量);
下面我们使用如下命令对/animal节点的值进行修改并添加一个子节点/animal/dog:
[zk: localhost:2181(CONNECTED) 8] set /animal "bb"
cZxid = 0x6
ctime = Mon Dec 14 11:03:43 CST 2020
mZxid = 0x1d
mtime = Wed Dec 16 20:25:26 CST 2020
pZxid = 0x1c
cversion = 5
dataVersion = 4
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 2
numChildren = 1
[zk: localhost:2181(CONNECTED) 9] create /animal/dog "cc"
Created /animal/dog
[zk: localhost:2181(CONNECTED) 10] get /animal
bb
cZxid = 0x6
ctime = Mon Dec 14 11:03:43 CST 2020
mZxid = 0x1d
mtime = Wed Dec 16 20:25:26 CST 2020
pZxid = 0x1e
cversion = 6
dataVersion = 4
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 2
numChildren = 2
如上可以看到,mZxid、mtime、dataVersion、numChildren、cversion都发生了变化。简单理解,可以将zookeeper类似Linux文件系统,一棵树结构,所有节点就类似树节点。
二、节点类型
zookeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
- 临时节点
该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。
- 持久化节点
该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
三、节点特性
【a】同一级节点 key 名称是唯一的
在zookeeper中,规定同一级只能允许有一个key名称相同的文件目录,这一特性可以用来实现分布式锁。如:
[zk: localhost:2181(CONNECTED) 13] ls /animal
[cat0000000002, dog]
[zk: localhost:2181(CONNECTED) 14] create /animal/dog "dog"
Node already exists: /animal/dog #提示/animal/dog节点已存在,无法创建节点
如上可见,当前/animal节点下面已经存在 /dog节点,再次创建会提示已经存在。
【b】创建节点时必须使用全路径
在zookeeper中,当想在某个节点下面创建子节点时,需要带上全路径。例如:
[zk: localhost:2181(CONNECTED) 17] ls /animal
[cat0000000002, dog]
[zk: localhost:2181(CONNECTED) 18] create /animal/cat cat
Created /animal/cat
[zk: localhost:2181(CONNECTED) 19] create /animal/cat/cat_1 cat_1
Created /animal/cat/cat_1
[zk: localhost:2181(CONNECTED) 20] create /animal/cat/cat_2 cat_2
Created /animal/cat/cat_2
[zk: localhost:2181(CONNECTED) 21] ls /animal/cat
[cat_1, cat_2]
如下图所示:
【c】当客户端与服务器端断开连接时,临时节点将会被删除
下面我们打开一个终端一: 创建一个临时节点,使用-e参数即可。
[zk: localhost:2181(CONNECTED) 23] ls /animal
[cat0000000002, dog, cat]
[zk: localhost:2181(CONNECTED) 24] create -e /animal/monkey monkey
Created /animal/monkey
[zk: localhost:2181(CONNECTED) 25] ls /animal
[monkey, cat0000000002, dog, cat]
[zk: localhost:2181(CONNECTED) 26]
打开另外一个终端二: 查看此临时节点:
[zk: localhost:2181(CONNECTING) 0] ls /animal
[monkey, cat0000000002, dog, cat]
断开终端一与zookeeper的连接,即会话关闭:
[zk: localhost:2181(CONNECTED) 26] quit
Quitting...
2020-12-17 10:46:30,464 [myid:] - INFO [main:ZooKeeper@693] - Session: 0x1000004fa8f0000 closed
2020-12-17 10:46:30,612 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@522] - EventThread shut down for session: 0x1000004fa8f0000
此时,再次使用终端二重新查看此节点:
[zk: localhost:2181(CONNECTED) 1] ls /animal
[dog, cat]
我们发现,终端二已经查询不到这个临时节点了。注意同时cat0000000002节点也是临时节点来着,所以cat0000000002和monkey节点都不存在了。
【d】节点具有watch 机制,可以监听节点变化
事件监听机制类似于观察者模式,watch 监听流程是客户端向服务端某个节点路径上注册一个 watcher,同时客户端也会存储特定的 watcher,当节点数据或子节点发生变化时,服务端通知客户端,客户端进行回调处理。
特别注意:监听事件被单次触发后,事件就失效了。
【e】删除节点只能一级一级删除
如果我们删除一个存在子节点的节点,zookeeper会提示节点非空,不能删除。
[zk: localhost:2181(CONNECTED) 2] ls /animal
[dog, cat]
[zk: localhost:2181(CONNECTED) 3] delete /animal
Node not empty: /animal
不过,新版本可以通过 deleteall 命令递归删除。
四、总结
本篇文章主要总结了Zookeeper中节点znode的结构,其实我们把它类比成树的节点即可,并且总结了zookeeper节点的一些特性,通过这些特性,我们可以灵活应用到项目中的很多场景,如:
- 分布式锁
- 统一配置管理
- 集群管理
- 分布式协调/通知
- 数据发布/订阅
- 负载均衡