目录
session
https://zookeeper.apache.org/doc/r3.1.2/zookeeperStarted.html
基础概念
- 一个客户端链接生成一个 session,由 server 生成唯一的 sessionId
- tickTime 客户端通过tickTime(心跳时间)来保持会话
- 当超过 会话超时时候见(ticktime * 2)未收到心跳,则认为客户端断开连接
- 会话请求的处理按照 FIFO 进行处理
客户端连接
2020-04-12 23:36:27,418 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000584c2f70003, negotiated timeout = 30000
数据模型
层次命名空间
- 类似unix文件系统,以(/)为根
- 区别:节点可以包含与之关联的数据以及子节点(既是文件也是文件夹)
- 节点的路径总是表示为规范的、绝对的、斜杠分隔的路径
Znode
概念
每一个节点被称为 znode
- 名称唯一,命名规范
- 节点类型:持久、顺序、临时、临时顺序
- 节点由数据构成
节点类型
# 持久接地那
[zk: localhost:2181(CONNECTED) 15] create /gzy_01
Created /gzy_01
# 临时节点
[zk: localhost:2181(CONNECTED) 16] create -e /gzy_02
Created /gzy_02
# 顺序节点
# 1. 拥有一个 10 位的十进制序号 2. 每个父节点拥有一个计数器(各自独立)
[zk: localhost:2181(CONNECTED) 20] create /gzy_03
Created /gzy_03
[zk: localhost:2181(CONNECTED) 21] create -s /gzy_03/
Created /gzy_03/0000000000
[zk: localhost:2181(CONNECTED) 22] create -s /gzy_03/
Created /gzy_03/0000000001
[zk: localhost:2181(CONNECTED) 23] create -s /gzy_03/
Created /gzy_03/0000000002
# 临时顺序节点
[zk: localhost:2181(CONNECTED) 24] create -s -e /gzy_03/
Created /gzy_03/0000000003
数据构成
- 存储了协调数据
- 协调数据:节点信息、配置、位置信息等
- 节点的元数据
- 数据上限:1M
节点元数据
[zk: localhost:2181(CONNECTED) 35] ls -s /gzy_01
[]cZxid = 0xc
ctime = Mon Apr 13 00:02:05 CST 2020
mZxid = 0x15
mtime = Mon Apr 13 00:09:09 CST 2020
pZxid = 0xc
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
ACL
https://zookeeper.apache.org/doc/r3.6.0/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
- ACLs are made up of pairs of (scheme:expression, perms).
- For example, the pair (ip:19.22.0.0/16, READ) gives the READ permission to any clients with an IP address that starts with 19.22.
ZK 中的时间
- Zxid
- 每次更改操作都对应一个唯一的事务id,称为 zxid
- 它是一个全局有序的戳记,如果 zxid1 小于 zxid2,则 zxid1 发生在 zxid2 之前。
- Version numbers 版本号
- 对节点的每次更改都会导致该节点的版本号之一增加。
- 修改数据时,如果 version 小于服务器 version,则无法修改
- Ticks
- 当使用多服务器ZooKeeper时,服务器使用“滴答” 来定义事件的时间,如状态上传、会话超时、对等点之间的连接超时等。
- 滴答时间仅通过最小会话超时(滴答时间的2倍)间接公开
- 如果客户端请求的会话超时小于最小会话超时,服务器将告诉客户端会话超时实际上是最小会话超时。
- 例如使用 client 连接 server 时候,自定义了超时时间
- Real time
- 除了在znode创建和修改时将时间戳放入 stat 结构之外,根本不使用 Real time 或时钟时间。
※watch
- 可以通过设置 watch,来监听 znode 的变化
监听机制
- data watch
- 监听节点的变化
- child watch
- 监听子节点的变化
触发事件
We can set watches with the three calls that read the state of ZooKeeper: exists, getData, and getChildren. The following list details the events that a watch can trigger and the calls that enable them:
- Created event
- Enabled with a call to exists.
- Deleted event
- Enabled with a call to exists, getData, and getChildren.
- Changed event
- Enabled with a call to exists and getData.
- Child event
- Enabled with a call to getChildren.
使用时机
- getData()
- getChild()
- exists()
[zk: localhost:2181(CONNECTED) 17] help
stat path [watch]
ls path [watch]
ls2 path [watch]
get path [watch]
重要特性
- 一次有效
- watch 被触发一次后,会立即失效
- 顺序性
- 客户端先得到 watch 通知,后看到 watch 结果
注意事项
- 一次性触发器
- 如果一个 watch 被触发后,还希望关注未来的事件,则需要重新设置 watch
- 不可靠
- 因为 watch 是一次性触发器,并且在获取事件和发送新的 watch 请求之间有延迟。所以不可能可靠的监听到每个节点的更改
- 一个节点监听多个事件,只会被触发一次
- 如果一个 watch 对象同时注册了 getData/exists,当节点被删除,删除事件对 getData/exists 都有效,但只会被调用一次
ZK的特性
- 顺序一致性(Sequential Consistency)
- 保证客户端操作是按顺序生效的。
- 原子性(Atomicity)
- 更新成功或失败,没有部分结果。
- 单个系统映像
- 无论连接到哪个服务器,客户端都将看到相同的内容。
- 可靠性
- 数据的变更不会丢失,除非被客户端覆盖修改。,
- 及时性
- 保证系统的客户端当时读取到的数据是最新的。
代码演示
ZkSerializer
可以定义序列化和反序列化的逻辑
public class MyZkSerializer implements ZkSerializer {
String charset = "UTF-8";
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
try {
return new String(bytes, charset);
} catch (UnsupportedEncodingException e) {
throw new ZkMarshallingError(e);
}
}
public byte[] serialize(Object obj) throws ZkMarshallingError {
try {
return String.valueOf(obj).getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new ZkMarshallingError(e);
}
}
}
ZkWatch
监听节点变化
public class ZkClientWatchDemo {
static class MyDataListener implements IZkDataListener{
@Override
public void handleDataChange(String path, Object data) throws Exception {
System.out.println("数据变化.path:" + path +" | data:" + data);
}
@Override
public void handleDataDeleted(String s) throws Exception {
System.out.println("节点被删除.path:" + s);
}
}
public static void main(String[] args) {
// 创建一个zk客户端
ZkClient client = new ZkClient("localhost:2181");
client.setZkSerializer(new MyZkSerializer());
client.subscribeDataChanges("/gzy_01",new MyDataListener());
System.out.println("init success.");
try {
Thread.sleep(1000 * 60 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}