1.Zookeeper 是什么?
官⽅⽹址:https://zookeeper.apache.org/
- ZooKeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步和提供组服务。
- ZooKeeper主要用来解决 分布式应用 中经常遇到的一些 数据管理 功能。
2.ZooKeeper 核心概念
2.1 文件系统的数据模型
ZooKeeper 维护一个 类似文件系统的,树型的 数据模型。
每个⼦⽬录项都被称作为 node(⽬录节点),和⽂件系统类似,我们能够⾃由的增加、删除 node,在⼀个node下增加、删除⼦node。
⑴ 创建节点:
[zk: localhost:2181(CONNECTED) 0] create /app1
Created /app1
[zk: localhost:2181(CONNECTED) 1] create /app2
Created /app2
[zk: localhost:2181(CONNECTED) 2] create /app1/node1
Created /app1/node1
[zk: localhost:2181(CONNECTED) 3] create /app1/node2
Created /app1/node2
[zk: localhost:2181(CONNECTED) 4] create /app1/node3
Created /app1/node3
⑵ 递归的查看所有节点:
[zk: localhost:2181(CONNECTED) 5] ls -R /
/
/app1
/app2
/zookeeper
/app1/node1
/app1/node2
/app1/node3
/zookeeper/config
/zookeeper/quota
2.2 目录节点类型
目前有 6 种类型的 node:(3.5.x版本以前只有前⾯四种):
⑴ 持久化 目录节点(Persistent Node)
- 特点:客户端与zookeeper断开连接后,该节点依旧存在;只要不⼿动删除该节点,他将永远存在。
[zk: localhost:2181(CONNECTED) 6] create /persistent
Created /persistent
⑵ 持久化 顺序编号 目录节点 (Persistent Sequential Node)
- 具有 持久化 目录节点 的特点;
- Zookeeper 给该节点名称进⾏顺序编号
[zk: localhost:2181(CONNECTED) 7] create -s /persistent/seq
Created /persistent/seq0000000001
[zk: localhost:2181(CONNECTED) 8] create -s /persistent/seq
Created /persistent/seq0000000002
[zk: localhost:2181(CONNECTED) 9] create -s /persistent/seq
Created /persistent/seq0000000003
注意,这个 0000000001 是十进制的。
⑶ 临时 目录节点 (Ephemeral Node)
- 特点:客户端与zookeeper断开连接后,该节点被删除;
[zk: localhost:2181(CONNECTED) 10] create -e /ephemeral
Created /ephemeral
注意:临时 目录节点 不能创建 子结点:
[zk: localhost:2181(CONNECTED) 11] create /ephemeral/child
Ephemerals cannot have children: /ephemeral/child
[zk: localhost:2181(CONNECTED) 12] create -e /ephemeral/child
Ephemerals cannot have children: /ephemeral/child
[zk: localhost:2181(CONNECTED) 13] create -s /ephemeral/child
Ephemerals cannot have children: /ephemeral/child
[zk: localhost:2181(CONNECTED) 14] create -e -s /ephemeral/child
Ephemerals cannot have children: /ephemeral/child
在 临时 目录节点上创建各种类型的子结点都失败了!
退出客户端,再重启客户端,并查看目录节点:
[zk: localhost:2181(CONNECTED) 15] quit
root@bbeb695a7013:/apache-zookeeper-3.5.9-bin/bin# zkCli.sh
[zk: localhost:2181(CONNECTED) 0] ls /
我们发现,/ephemeral
目录节点消失了,这正符合 临时 目录节点 的特点。
⑷ 临时 顺序编号 目录节点 (Ephemeral Sequential Node)
- 具有 临时 目录节点 的特点;
- 是Zookeeper给该节点名称进⾏顺序编号
① 在 容器节点 下创建 临时顺序编号目录节点 :
[zk: localhost:2181(CONNECTED) 2] create -c /container
Created /container
[zk: localhost:2181(CONNECTED) 3] create -e -s /container/
Created /container/0000000000
[zk: localhost:2181(CONNECTED) 4] create -e -s /container/
Created /container/0000000001
[zk: localhost:2181(CONNECTED) 5] quit
这还挺方便的,退出的时候,所有临时结点连同容器结点一同消失了。
② 在 持久化顺序节点 下创建 临时顺序编号目录节点 :
[zk: localhost:2181(CONNECTED) 0] create -s /a
Created /a0000000010
[zk: localhost:2181(CONNECTED) 4] create -s -e /a0000000010/
Created /a0000000010/0000000000
多级编号目录节点,可能会有用吧。
⑸ 容器 节点 (Container Node,3.5.3 版本新增)
- 如果Container节点下⾯没有⼦节点,则Container节点在未来会被Zookeeper⾃动清除,定时任务默认60s 检查⼀次
create -c /container
注意:
容器节点主要⽤来容纳字节点,
- 如果没有给其创建⼦节点,容器节点表现和持久化节点⼀样;
- 如果给容器节点创建了⼦节点,后续⼜把⼦节点清空,容器节点也会被zookeeper删除。
⑹ 定时过期 节点 (TTL Node,3.5.3 版本新增)
- 默认禁⽤,只能通过系统配置 zookeeper.extendedTypesEnabled=true 开启,不稳定
2.3 创建结点时的易错点
⑴ 无法一次创建多级目录节点
[zk: localhost:2181(CONNECTED) 0] create /a/b/c
Node does not exist: /a/b/c
即创建子结点时,父结点必须已经创建好。
⑵ 除了创建顺序节点,否则不可以以/
作为路径的结尾:
[zk: localhost:2181(CONNECTED) 8] create /a/
Path must not end with / character
⑶ 临时目录节点 不能创建子结点:
[zk: localhost:2181(CONNECTED) 0] create /ephemeral/child
Ephemerals cannot have children: /ephemeral/child
⑷ 目录节点的路径必须以 /
开头:
[zk: localhost:2181(CONNECTED) 0] create -c container
Path must start with / character
3. 实战
3.1 乐观锁
根据状态数据中的版本号 dataVersion 有并发修改数据实现乐观锁的功能。
首先,创建一个 /test-node
目录节点:
[zk: localhost:2181(CONNECTED) 0] create /test-node a
Created /test-node
然后,用 get -s /test-node
或者 stat /test-node
可以查看状态信息:
dataVersion 初始值为 0。接着,每次修改 节点数据时,zookeeper 都会递增 dataVersion。
如果在修改节点数据时指定版本号。那么并发执行时,只有一个请求能修改成功,其他请求都会提示 version No is not valid : /test-node:
4.事件监听机制
可以带监听参数 -w
的命令有 config [-w]
, get [-w] path
, ls [-w] path
, stat [-w] path
- 针对节点的监听:⼀旦事件触发,对应的注册⽴刻被移除,所以事件监听是⼀次性的。
4.1 监听子目录的变化
首先,监听子目录的变化,用的是命令 ls [-w] path
:
[zk: localhost:2181(CONNECTED) 0] create /test
Created /test
[zk: localhost:2181(CONNECTED) 1] ls -w /test
[]
[zk: localhost:2181(CONNECTED) 2] create /test/sub1
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/test
Created /test/sub1
[zk: localhost:2181(CONNECTED) 3] create /test/sub2
Created /test/sub2
- ⼀旦事件触发,对应的注册⽴刻被移除,所以事件监听是⼀次性的。所以创建路径为
/test/sub1
的结点时,事件监听被触发,但是创建路径为/test/sub2
的结点时,就不会再次触发事件监听了;
[zk: localhost:2181(CONNECTED) 4] ls -w /test
[sub1, sub2]
[zk: localhost:2181(CONNECTED) 5] delete /test/sub1
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/test
- 再次注册事件监听,当删除路径为
/test/sub1
的结点时,事件监听又被触发了;
[zk: localhost:2181(CONNECTED) 0] create /test/abc
Created /test/abc
[zk: localhost:2181(CONNECTED) 1] ls -w /test
[abc, sub2]
[zk: localhost:2181(CONNECTED) 2] create /test/abc/123
Created /test/abc/123
ls -w /test
只能监听/test
的自身及直接子目录的变化。但是,不能监听子目录的子目录的变化。所以创建目录为/test/abc/123
的结点不会触发对/test
子目录的监听事件。
如果,我们要递归地监听所有的子目录的变化,需要使用 ls -w -R /test
命令:
[zk: localhost:2181(CONNECTED) 24] create /test
Created /test
[zk: localhost:2181(CONNECTED) 25] create /test/abc
Created /test/abc
[zk: localhost:2181(CONNECTED) 26] ls -w -R /test
/test
/test/abc
[zk: localhost:2181(CONNECTED) 27] create /test/abc/123
WATCHER::Created /test/abc/123
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/test/abc
4.2 监听数据的变化
使用 create path [data]
可以在创建节点时,为节点赋予初始数据。再通过 set path data
来修改路径为 path 的节点的数据。
[zk: localhost:2181(CONNECTED) 28] create /data a
Created /data
[zk: localhost:2181(CONNECTED) 29] get -w /data
a
[zk: localhost:2181(CONNECTED) 30] set /data b
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/data
我们通过 get -w /data
监听路径为 /data
的节点的数据变化。
另外,从我目前测试结果来看, stat -w path
也是用来监听数据变化的,不能监听子目录的变化,不能监听ACL权限的变化。