分布式专题(11)之Zookeeper特性与节点数据类型详解
一、Zookeeper数据结构
Zookeeper数据模型与结构与Unix文件系统很类似,整体上可以看做是一棵树,每个节点称做一个ZNode。
Zookeeper的数据模型是层次模型,层次模型常见于文件系统 。层次模型和Key-Value模型是两种主流的数据模型,Zookeeper使用文件系统模型主要以下两点考虑:
- 文件系统的树形结构便于表达数据之间的层次关系;
- 文件系统的树形结构便于为不同的应用分配独立的命名空间(namespace)
Zookeeper的层次模型称做Data Tree,Data Tree的每个节点叫做ZNode。不同于文件系统,每个节点都可以保存数据,每个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径的唯一标记,每个节点都有一个版本(Version),版本从0开始计数;
public class DataTree {
private final ConcurrentHashMap<String, DataNode> nodes =
new ConcurrentHashMap<String, DataNode>();
private final WatchManager dataWatches = new WatchManager();
private final WatchManager childWatches = new WatchManager();
}
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
}
1.1 节点分类
zookeeper存在几种不同的节点类型,他们具有不同的生命周期:
类型 | 生命周期 | 创建示例 |
持久节点 (persistent node) | 一直存在,一直存储在ZooKeeper 服务器上,即使创建该节点的客户端与服务端的会话关闭了,该节点依然不会被删除 | create /locks |
临时节点 (ephemeral node) | 当创建该临时节点的客户端会话因超时或发生异常而关闭时,该节点也相应在 ZooKeeper 服务器上被删除。 | create -e /locks/DBLock |
有序节点 (sequential node) | 并不算是一种单独种类的节点,而是在之前提到的持久节点和临时节点特性的基础上,增加了一个节点有序的性质。在我们创建有序节点的时候会自动使用一个单调递增的数字作为后缀 | create -e -s /jobs/job (临时有序节点) |
容器节点 (container node) | 当一个容器节点的最后一个子节点被删除后,容器节点也会被删除 | create -c /work |
TTL节点 (ttl node) | 当一个TTL节点在 TTL 内没有被修改并且没有子节点,会被删除。注意:默认此功能不开启,需要修改配置文件extendedTypesEnabled=true | create -t 3000 /ttl_node |
一个znode可以拥有持久性,也可以是临时性的:
- 持久节点(PERSISTENT)这样的znode在创建之后即使发生zookeeper集群宕机也不会丢失;
- 临时节点(EPHEMERAL)client宕机或者client在指定的timeout时间内没有给zookeeper集群发消息,这样的znode就会消失;
如果上面两种znode具备顺序性,又有一下两种znode:
- 持久顺序节点(PERSISTENT_SEQUENTIAL):znode除了具备持久的特点外,znode的名字具备顺序性;
- 临时顺序节点(EPHEMERAL_SEQUENTIAL):znode除了具备znode的特点外,znode名字还具备顺序性;
zookeeper主要用到的是以上4种节点。
- Container节点 (3.5.3版本新增):Container容器节点,当容器中没有任何子节点,该容器节点会被zk定期删除(定时任务默认60s 检查一次)。 和持久节点的区别是 ZK 服务端启动后,会有一个单独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点。可以用于 leader 或者锁的场景中。
1.2 节点状态信息
类似树状结构,节点下面是可以存储一些信息和属性的,可以通过stat命令来查看。
- cZxid:Znode创建的事务id;
- ctime:节点创建时的时间戳;
- mZxid:Znode被修改的事务id,即每次 对znode的修改都会更新mZxid;
对于zk来说,每次变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id),通过Zxid可以确定更新操作的先后顺序,例如:如果zxid1小于zxid2,说明zxid操作先zxid2发生,zxid对于整个zk都是唯一的,及时操作的是不同的znode;
- pZxid:表示该节点的子节点列表最后一次修改的事务ID,添加子节点或者删除子节点就会影响子节点列表,但是修改子节点的数据内容不影响该ID(注意:只有子节点列表变更了才会变更pzxid,子节点内容变更不会影响pzxid)
- cversion:子节点的版本号,当zonode的子节点有变化时,cversion的值就会增加1;
- ephemeralOwner:如果该节点伟临时节点,ephemeralOwner的值表示与该节点绑定的session id。如果不是,ephemeralOwner值为0(持久化节点);
在client和server通信之前,首先需要建立连接,该连接称做是session。连接建立之后,如果发生连接超时、授权失败或者显示关闭连接,连接便会处于closed状态,此时session结束。
- dataLength:数据的长度;
- numClidren:子节点的数量(只统计直接子节点的数量)
3.3 监听机制
watch机制,顾名思义是一个监听机制。Zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。
- None: 连接建立事件
- NodeCreated: 节点创建
- NodeDeleted: 节点删除
- NodeDataChanged:节点数据变化
- NodeChildrenChanged:子节点列表变化
- DataWatchRemoved:节点监听被移除
- ChildWatchRemoved:子节点监听被移除
特性 | 说明 |
一次性触发 | watch是一次性的,一旦被触发就会移除,再次使用时需要重新注册 |
客户端顺序回调 | watch回调是顺序串行执行的,只有回调后客户端才能看到最新的数据状态。一个watcher回调逻辑不应该太多,以免影响别的watch执行 |
轻量级 | WatchEvent是最小的通信单位,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容 |
时效性 | watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知; |
3.3.1 永久性watch
在被触发之后,仍然保留,可以继续监听ZNode上的变更,是Zookeeper 3.6.0版本新增的功能。
addWatch [-m mode] path
addWatch的作用是针对指定节点添加事件监听,支持两种模式:
- PERSISTENT,持久化订阅,针对当前节点的修改和删除事件,以及当前节点的子节点的删除和新增事件。
- PERSISTENT_RECURSIVE,持久化递归订阅(默认),在PERSISTENT的基础上,增加了子节点修改的事件触发,以及子节点的子节点的数据变化都会触发相关事件(满足递归订阅特性)