其他分享
首页 > 其他分享> > 【深入 ZooKeeper】— ZooKeeper 一文通

【深入 ZooKeeper】— ZooKeeper 一文通

作者:互联网

ZooKeeper 为分布式应用提供了高效且可靠的分布式协调服务,例如统一命名服务、配置管理和分布式锁等分布式的基础服务。另外,在解决分布式一致性方面,ZooKeeper 并没有直接采用 Paxos 算法,而是采用一种被称为 ZAB(ZooKeeper Atomic Broadcast)的一致性协议。

基本概念

ZooKeeper 有以下几个核心概念

集群角色

ZooKeeper 中存在三种角色:Leader、Follower 和 Observer。ZooKeeper 集群中的所有机器通过一个 Leader 选举过程来选定一台被称为“Leader”的机器,Leader 服务器为客户端提供读和写服务。除 Leader 外,其他机器包括 Follower 和 Observer。Follower 和 Observer 都能提供读服务,唯一的区别在于,Observer 机器不参与 Leader 选举过程,也不参与写操作的“过半写成功”的策略。

会话

Session 是指客户端会话,与之相关的便是客户端连接。在 ZooKeeper 中,一个客户端连接是指客户端和服务器之间的一个 TCP 长连接。客户端启动的时候,首先会与服务器建立一个 TCP 连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watch 事件通知。Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间。当由于各种原因(服务器压力太大、网络故障或是客户端主动断开)导致客户端连接断开时,只要在 sessionTimeout 规定时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

数据节点

在 ZooKeeper 中,节点分为两种

对于第一种我们好理解,也就是分布式中的机器节点。第二种便是 ZooKeeper 中的数据存储模型,称之为数据节点——ZNode。ZooKeeper 将所有的数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个 ZNode,例如 /root/path。每个节点上都会保存自己的数据内容,同时还有保存一系列属性信息。

ZNode 分为持久节点和临时节点两类。所谓持久节点是指一旦这个 ZNode 被创建了,除非主动进行 ZNode 的移除操作,否则这个 ZNode 将一直保存在 ZooKeeper 上。而临时节点不一样,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。

ZooKeeper 还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,ZooKeeper 会自动在其节点后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。

所以,ZooKeeper 中的 ZNode 有以下 4 中类型

需要注意的是,ZooKeeper 规定不能基于临时节点来创建子节点,也就是说临时节点只能作为叶子节点。

版本

ZooKeeper 中为数据节点引入了版本的概念,每个数据节点都具有三种类型的版本信息,分别是 version、cversion 和 aversion,对数据节点的任何更新操作都会引起版本号的变化。ZooKeeper 为每个数据节点维护一个叫做 Stat 的数据结构,Stat 中记录了这三个数据版本,版本信息介绍如下

版本类型 说明
version 当前数据节点数据内容的版本号
cversion 当前数据节点子节点的版本号
aversion 当前数据节点 ACL 变更版本号

ZooKeeper 中的版本概念和传统意义上的软件版本有很大的区别,它表示的是对数据节点的数据内容、子节点列表,或是节点 ACL 信息的修改次数。举个栗子,节点 /somelogs/article 被创建的时候,version 为0,表示该节点从创建以来,被修改了 0 次。如果现在对该节点内容进行修改,version 则变成 1。这里需要注意的是,version 表示的是修改的次数,跟修改的数据内容没关系。也就是说,即使两次更新后,数据内容没有发现变化,version 依旧会变。

那 ZooKeeper 引入版本的作用是什么呢?大概你也猜到了,就是乐观锁了。不管是数据库的乐观锁,还是 JDK 中 Unsafe 类的 compareAndSwap 操作,都是类似的思想。ZooKeeper 在每一次数据更新的时候,都会把当前请求的版本和服务器上对应数据节点的最新版本作比较,如果不一致,将会抛出 BadVersionException 异常。当然,如果在更新的时候指定 version 为 -1,表示客户端想忽略版本比较,直接基本当前最新版本进行更新。数据更新源代码如下

version = setDataRequest.getVersion();
int currentVersion = nodeRecord.stat.getVersion();
if (version != -1 && version != currentVersion) {
    throw new KeeperException.BadVersionException(path);
}
version = currentVersion + 1;

一句话概括版本的作用:保证分布式数据原子性操作

Watcher

ZooKeeper 可以提供了分布式数据的发布/订阅功能。一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能够让多个订阅者同时监听某一个主题对象,当这个主题对象发生变化时,会通知所有订阅者,使他们能够做出相应的处理。在 ZooKeeper 中,引入了 Watcher 机制来实现这种分布式的通知功能。ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些指定事件触发这个监听,那么就会想指定客户端发送一个事件通知来实现分布式的通知功能。

Watcher 具有以下特性

一次性

一旦一个 Watcher 被触发,ZooKeeper 都会将其从相应的存储中移除。因此,开发人员在
Watcher 的使用上要记住一点事需要反复注册。这样的设计有效的减轻了服务端的压力。

客户端串行执行

客户端 Watcher 回调过程是一个串行同步的过程,这为我们保证了顺序。所以千万不要为了一个 Watcher 的处理逻辑影响了这个客户端的 Watcher 回调。

轻量

Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容,也就是说需要客户端主动重新去获取——这也是 ZooKeeper 的 Watcher 机制的一个非常重要的特性。如此轻量的 Watcher 机制设计,在网络开销和服务端内存开销上都是非常廉价的。

ACL

为了有效地保障节点数据的安全,避免误操作带来的数据随意变更而导致分布式系统出现异常,ZooKeeper 提供了一套完善的 ACL(Access Control List)权限控制机制来保障数据的安全。

ZooKeeper 提供了如下 5 中权限。

需要注意的是,CREATE 和 DELETE 权限都是针对子节点列表的权限控制。

API 使用

Java 客户端 API 使用的例子都在我的 github

Curator

Curator 是 ZooKeeper 的一个开源客户端,不仅为开发者提供了更为便利的 API 接口,而且还提供了一些典型场景的使用参考,有如下几种

事件监听

通过原生的 API 来进行事件监听,不是很方便,需要开发人员反复注册 Watcher,比较繁琐。Curator 能够自动为开发人员处理反复注册监听,从而大大简化了原生 API 开发的繁琐过程。

Master 选举

在分布式系统中,经常会碰到这样的场景:对于一个复杂的任务,仅需要从集群中选举出一台进行处理即可。诸如此类的分布式问题,我们统称为“Master 选举”。借助 ZooKeeper,我们可以比较方便地实现 Master 选举的功能,大体思路为:选择一个根节点,例如 /master_select,多台机器同时向该节点创建一个节点 /master_select/lock,利用 ZooKeeper 的特性,最终只有一台机器能创建成功,成功的那台机器就作为 Master

分布式锁

分布式锁时控制分布式系统之间同步访问共享资源的一种方式。ZooKeeper 实现分布式锁的思路就是在指定的目下生成临时有序节点,序号最小的那个就获得锁。锁释放的时候,只要将该瞬时节点删除即可。这里需要注意“羊群效应”。

分布式计数器

ZooKeeper 实现分布式计数器的思路如下:指定一个数据节点作为计数器,多个应用实例在分布式锁的控制下,通过更新该数据节点的内容来实现技术功能。

有关上面场景的代码在我的 github 上。

参考

《从Paxos到Zookeeper:分布式一致性原理与实践》

标签:一文,ZooKeeper,Watcher,深入,客户端,数据,节点,分布式
来源: https://www.cnblogs.com/tailife/p/16364377.html