前言:

Zookeeper的视图结构和标准的UNIX文件系统类似,整个结构也是以树形目录结构展现的。

Zookeeper中的每个节点称为ZNode,每个ZNode上既可以保存数据,也可以挂载子节点。

关于ZNode,不仅可以存储数据,节点本身也有一些状态信息(Stat),本文就来分析下这个Stat信息。

1.Stat信息展示

我们随意创建一个节点,可以使用Zookeeper客户端命令,如下所示:

# 1.创建节点/nodefirst 并设置值为:'2022-06-19'
[zk: localhost:2181(CONNECTED) 3] create /nodefirst '2022-06-19'
Created /nodefirst

# 2.获取节点信息
[zk: localhost:2181(CONNECTED) 5] get /nodefirst
2022-06-19  # 节点值

# 以下为Stat信息
cZxid = 0x4
ctime = Sun Jun 19 15:40:10 GMT+08:00 2022
mZxid = 0x4
mtime = Sun Jun 19 15:40:10 GMT+08:00 2022
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0

我们来逐个看下这些参数的含义

1.1 cZxid/ctime

cZxid即create zxid的缩写(创建该节点的事务ID),每一次事务操作都会有一个特定的zxid来代表当前事务ID。

这个值在节点创建成功后是不会变的

ctime顾名思义也就是 created time的缩写(该节点创建时间),这个值在节点创建成功后也是不会变的。

1.2 mZxid/mtime

mZxid即modified zxid的缩写(节点最后一次被更新时的事务ID),每一次更新该节点操作都会修改该值

mtime对应的也就是 modified time的缩写(节点最后一次被更新时的时间)。

那我们再来测试下,执行下set命令,看下节点信息是否修改

# 修改/nodefirst 值为 '202206191550'
[zk: localhost:2181(CONNECTED) 6] set /nodefirst '202206191550'
cZxid = 0x4
ctime = Sun Jun 19 15:40:10 GMT+08:00 2022
mZxid = 0x5
mtime = Sun Jun 19 15:50:35 GMT+08:00 2022
pZxid = 0x4
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0

从结果可以看到,cZxid/ctime没有发生变化,而mZxid/mtime则发生了变化,mZxid增加1,mtime则为当前时间

1.3 pZxid

表示该节点的子节点列表最后一次被修改时的事务ID。

这个理解起来还是有点抽象的,直接通过示例来展示下。

# 创建节点/nodefirst 的子节点 level2_node1
[zk: localhost:2181(CONNECTED) 7] create /nodefirst/level2_node1 'node1'
Created /nodefirst/level2_node1

# 查看/nodefirst节点信息
[zk: localhost:2181(CONNECTED) 9] get /nodefirst
202206191550
cZxid = 0x4
ctime = Sun Jun 19 15:40:10 GMT+08:00 2022
mZxid = 0x5
mtime = Sun Jun 19 15:50:35 GMT+08:00 2022
pZxid = 0x6
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 1

在创建子节点之前/nodefirst的pZxid=0x4,创建子节点后pZxid = 0x6

再次创建一个子节点

[zk: localhost:2181(CONNECTED) 10] create /nodefirst/level2_node2 'node2'
Created /nodefirst/level2_node2

[zk: localhost:2181(CONNECTED) 11] get /nodefirst
202206191550
cZxid = 0x4
ctime = Sun Jun 19 15:40:10 GMT+08:00 2022
mZxid = 0x5
mtime = Sun Jun 19 15:50:35 GMT+08:00 2022
pZxid = 0x7
cversion = 2
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 2

pZxid变更为0x7

那么修改子节点的值呢

[zk: localhost:2181(CONNECTED) 12] set /nodefirst/level2_node2 'node22'
cZxid = 0x7
ctime = Sun Jun 19 15:58:23 GMT+08:00 2022
mZxid = 0x8
mtime = Sun Jun 19 15:59:24 GMT+08:00 2022
pZxid = 0x7
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

[zk: localhost:2181(CONNECTED) 13] get /nodefirst
202206191550
cZxid = 0x4
ctime = Sun Jun 19 15:40:10 GMT+08:00 2022
mZxid = 0x5
mtime = Sun Jun 19 15:50:35 GMT+08:00 2022
pZxid = 0x7
cversion = 2
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 2

pZxid=0x7,没有变化。

所以总结下来:节点的pzxid,只有子节点的列表变更了(子节点新增或删除)才会被修改,而子节点内容的变更不会影响pzxid

1.4 cversion/dataVersion/aclVersion

节点Stat信息中有三个version。

综合上面的示例:

/nodefirst值被修改过一次,所以当前dataVersion=1

/nodefirst的子节点被添加过两次,所以当前cversion=2

而ACL信息,一直没有修改过,所以aclVersion=0

1.5 ephemeralOwner

创建该临时节点会话的sessionId。

如果当前节点为持久节点,那么ephemeralOwner=0。

当前/nodefirst节点就是持久节点,所以ephemeralOwner = 0x0

1.6 dataLength

这个比较容易理解,就是当前节点数据内容的长度

当前/nodefirst值为202206191550,长度就是12

1.7 numChildren

表示当前节点的子节点的数量

当前节点/nodefirst有用两个子节点

[zk: localhost:2181(CONNECTED) 14] ls /nodefirst
[level2_node2, level2_node1]

所以/nodefirst 的numChildren = 2

2.节点Stat信息的作用

实际大部分时候我们都不会直接使用到节点的Stat信息。

倒是可以使用dataVersion信息的这种特性来做一个乐观锁。

回到Zookeeper.java代码中,来看下其中的setData()方法

/**
     * @param path
     *                the path of the node
     * @param data
     *                the data to set
     * @param version
     *                the expected matching version
     */
public Stat setData(final String path, byte data[], int version)
throws KeeperException, InterruptedException {

}

这里有一个入参version,这个version就是当前我们所希望的节点的dataVersion值。

Zookeeper服务端在处理该请求时,如果发现setData请求所希望version与当前实际的version不同(节点被执行过set操作),则报错。

06-20 15:10