本文为书籍《从Paxos到Zookeeper 分布式一致性原理与实践》倪超著_北京:电子工业出版社的读书笔记,这本书还是蛮值得推荐的。
一、数据模型
1.ZNode
ZK的视图结构和标准的UNIX文件系统非常类似,但是没有引入传统文件系统中的目录和文件等相关概念。
它是使用“数据节点”的概念,我们称之为ZNode。
ZNode是ZK中数据的最小单元。
每个ZNode上可以保存数据,同时也可以挂在子节点。
因此构成了一个层次化的命名空间,我们称之为树。
ZNode的节点路径标识方式和UNIX文件系统路径非常相似,都是由一系列使用斜杠 / 进行分割的路径标识。
2.事务ID
在ZK中,事务是指能够改变ZK服务器状态的操作,一般包括数据节点创建、删除、数据节点内容更新、客户端会话创建与失效等操作。
对于每一个事务请求,ZK都会为其分配一个全局唯一的事务ID,用ZXID表示,ZXID通常是一个64位的数字。
每一个ZXID对应一次更新操作,从这些ZXID中可以间接地识别出ZK处理这些更新操作请求的全局顺序。
二、节点(ZNode)特性
1.持久节点
该节点被创建后就会一直存在与ZK服务器上,直到有删除操作来主动清除这个节点
2.临时节点
临时节点的生命周期和客户端会话绑定在一起。
如果客户端会话失效,那么这个节点就会被自动清除掉。
ZK中不能基于临时节点来创建子节点,临时节点只能作为叶子节点。
3.持久顺序节点
基本特性与持久节点一致,额外特性表现在顺序性上。
ZK中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录每个子节点创建的先后顺序。
ZK会为给定节点名加上一个数字后缀,作为一个完整的、新的节点名。
4.临时顺序节点
基本特性和临时节点一致,在此基础上添加了顺序节点的特性
5.状态信息
每个数据节点除了存储数据内容以外,还存储了数据节点本身的一些状态信息。
ZK中Stat类的数据结构图
三、版本----保证分布式数据原子性操作
每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化。
当一个数据节点/zk-book被创建完毕后,节点version的值是0,表示的含义是“当前节点自从创建之后,被更新过0次”,如果现在对该节点的数据内容进行更新操作那么version就会变成1.
version表示的变更次数,即使前后两次变更操作并没有使得数据内容的值发生变化,version的值依然会变更。
版本信息主要是用来做乐观锁的,类似于JDK中的CAS。具体写入前校验的逻辑可以在PreRequestProcessor中看到。
四、Watcher----数据变更的通知
ZK允许客户端注册一个Watcher监听器,当服务端的一些指定时间触发了这个Watcher,那么就会向指定的客户端发送一个事件通知来实现分布式的通知功能。
1.工作机制
总的来说可以概括为3个过程:
1.客户端注册Watcher:
客户端向ZK服务器注册Watcher的同时,将Watcher对象存储在客户端的WatcherManager中。
2.服务端处理Watcher:
当ZK服务端触发Watcher事件后,会向客户端发送通知
3.客户端回调Watcher:
客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑
内部各组件之间的关系
2.Watcher接口
在ZK中,接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑。
包含KeeperState和EventType两个枚举类,分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)
3.Watcher事件
同一个事件在不同的通知状态中代表的含义不同
4. Watcher特定总结
1.一次性
无论服务端还是客户端,一旦一个Watcher被触发,ZK都会将其从相应的存储中移除。
因此开发人员在使用Watcher时需要反复注册(一些ZK的客户端已经实现了)
2.客户端串行执行
客户端Watcher回调的过程是一个串行执行的过程,保证了顺序。
因此开发人员写Watcher处理逻辑的时候主要不要因为一个Watcher处理逻辑太慢了影响了整个客户端Watcher的回调
3.轻量
WatchedEvent是ZK整个 Watcher通知机制的最小通知单元,这个数据结构中之包含三个部分:通知状态、事件类型、节点路径。
也就是说Watcher通知只会告诉客户端,某个事件发生了。而不会说数据节点的原始数据、变更后的数据是什么。需要客户端主动重新去获取数据。
客户端向服务端注册Watcher的时候,并不会把客户端真实的Watcher对象传递到服务端,仅仅只是在客户端请求中使用boolean类型的变量进行了标记,同时服务端也仅仅只是保存了当前连接的ServerCnxn对象。
这种轻量的Watcher设计机制,在网络开销和服务端内存开销上都比较廉价。
五、ACL----保障数据的安全
ZK的ACL权限控制和UNIX/LINUX操作系统中的ACL有一些区别,我们可以从三个方面来理解ACL机制:权限模式(Scheme)、授权对象(ID)、权限(Permission),通常使用“scheme:id:permission”来标识一个有效的ACL信息。
1.权限模式(Scheme)
权限模式用来确定权限验证过程中使用的检验策略。在ZK中常用的权限模式有如下几种:
IP
通过IP粒度来进行权限控制。例如配置了“ip:192.168.3.2”,即表示权限控制都是针对这个IP地址的。
同时IP模式也支持按照网段的方式进行配置,例如“ip:192.168.0.1/24”表示针对192.168.0.*这个IP段进行权限控制。
Digest
这是最常用的权限控制模式。
类似于"username:password"的形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。
当我们通过"username:password"形式配置了权限标识后,ZK会对其先后进行两次编码处理,分别是SHA-1算法加密和BASE64编码。其具体实现由DigestAuthenticationProvider.genenrateDigest(String idPassword)函数进行封装
World
是一种最开放的权限控制模式。
所有用户可以在不进行任何权限校验的情况下操作ZK上的数据。
是一种特殊的Digest,即"username:anyone"
Super
是一种特殊的Digest。
在该模式下,超级用户可以对任意ZK上的数据节点进行任何操作
2.授权对象(ID)
授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或是机器等。不同的权限模式下,授权对象不同。
3.权限(Permission)
权限就是指那些通过权限检查后可以被允许执行的操作。
ZK中对数据的操作权限分为以下5大类
4.权限扩展体系
ZK提供特殊的权限控制插件体系,允许开发人员通过指定方式对ZK的权限进行扩展。这些扩展的权限控制方式就像插件一样插入到ZK的权限体系中去。ZK官方文档中称该机制为“Pluggable ZooKeeper Authentication”