Zookeeper 系统模型

979 阅读9分钟

我正在参加「掘金·启航计划」

为了应对大流量,现代应用/中间件通常采用分布式部署,此时不得不考虑 CAP 问题。ZooKeeper是面向 CP 设计的一个开源的分布式协调框架,将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。

数据模型

ZooKeeper 有一个分层命名空间,很像分布式文件系统。唯一的区别是命名空间中的每个节点都可以有与其关联的数据以及子节点。ZooKeeper 树中的每个节点都称为 znode。znode 维护一个 stat 结构,其中包括数据更改的版本号、acl 更改。stat 结构也有时间戳。每次 znode 的数据更改时,版本号都会增加。例如,每当客户端检索数据时,它也会收到数据的版本。当客户端执行更新或删除时,它必须提供它正在更改的 znode 数据的版本。如果它提供的版本与数据的实际版本不匹配,则更新将失败。 zknamespace.jpg

节点特性

Zookeeper 的命名空间是由一系列的数据节点组成的。我们在本节详细讲解一下。

节点类型

在 ZooKeeper 中,每个数据节点都是有生命周期的,其生命周期的长短取决于数据节点的节点类型。在 3.6.0 版本之前,节点类型可以分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)和顺序节点(SEQUENTIAL)三大类,具体在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:

持久节点(PERSISTENT)

持久节点是 ZooKeeper 中最常见的一种节点类型。所谓持久节点,是指该数据节点被创建后,就会一直存在于 ZooKeeper 服务器上,直到有删除操作来主动清除这个节点。

持久顺序节点(PERSISTENT_SEQUENTIAL)

持久顺序节点的基本特性和持久节点是一致的,额外的特性表现在顺序性上。在 ZooKeeper 中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录下每个子节点创建的先后顺序。基于这个顺序特性,在创建子节点的时候,可以设置这个标记,那么在创建节点过程中,ZooKeeper 会自动为给定节点名加上一个数字后缀,作为一个新的、完整的节点名。另外需要注意的是,这个数字后缀的上限是整型的最大值。

临时节点(EPHEMERAL)

和持久节点不同的是,临时节点的生命周期和客户端的会话绑定在一起,也就是说,如果客户端会话失效,那么这个节点就会被自动清理掉。注意,这里提到的是客户端会话失效,而非 TCP 连接断开。另外,ZooKeeper 规定了不能基于临时节点来创建子节点。

临时顺序节点(EPHEMERAL_SEQUENTIAL)

临时顺序节点的基本特性和临时节点也是一致的,同样是在临时节点的基础上,添加了顺序的特性。

在 3.6.0 版本又加入了下面两种节点:

容器节点(Container Nodes)

容器节点的表现和持久节点是一样的,区别在于 ZooKeeper 服务端启动后,会有一个单独的线程去扫描所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点,除此之外和持久节点没有区别。

Container znodes are special purpose znodes useful for recipes such as leader, lock, etc.

官方文档建议容器节点可用于领导者、锁定等场景。

TTL 节点(TTL Nodes)

创建 PERSISTENT 或 PERSISTENT_SEQUENTIAL 类型的节点时,可以选择为 znode 设置一个 TTL(以毫秒为单位)。如果 znode 在 TTL 内没有被修改并且没有子节点,它将成为将来某个时候被服务器删除。

状态信息

Zookeeper 的每个数据节点除了存储了数据内容之外,还存储了数据节点本身的一些状态信息。我们可以用 stat 命令查看节点的状态信息。

企业微信截图_16656524418227.png

上图展现的字段其实就是数据节点的 Stat 对象的格式化输出,可以看下图 Stat 里面的属性解释。 CmQUOV-_wvWETeFwAAAAACpt4Uw454634825.jpg

版本

ZooKeeper 中为数据节点引入了版本的概念,每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化。

CmQUOF-_wvWEH85FAAAAABoRONU707630128.jpg

它表示的是对数据节点的数据内容、子节点列表,或是节点ACL信息的修改次数,每次更新之后版本号就会加 1。

在 ZooKeeper 中,version 属性正是用来实现乐观锁机制中的“写入校验”的。当更新数据时 version 的传值不为 -1,需要校验传入的 version 是否与当前节点的 version 值相等,如果不相等这次更新操作就会失败。

// Set the data for the node of the given path if such a node exists and the given version matches the version of the node (if the given version is -1, it matches any node's versions).
Stat setData(String path, byte[] data, int version)

Watch

ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。

watch.png

特性

ZooKeeper 的 Watcher 具有以下几个特性:

一次性

从上面的介绍中可以看到,无论是服务端还是客户端,一旦一个 Watcher 被触发,ZooKeeper 都会将其从相应的存储中移除。因此,开发人员在 Watcher 的使用上要记住的一点是需要反复注册。这样的设计有效地减轻了服务端的压力。如果注册一个Watcher之后一直有效,那么针对那些更新非常频繁的节点,服务端会不断地向客户端发送事件通知,这无论对于网络还是服务端性能的影响都非常大。注意:3.6.0版本加入了支持持久化和递归特性的 Watcher,这个 Watcher 在触发后不会被移除,监视触发事件类型有 NodeCreated、NodeDeleted 和 NodeDataChanged,并且可以选择递归的监视注册的 znode 开始的所有 znode,并且这种不会触发 NodeChildrenChanged 事件。

客户端串行执行

客户端 Watcher 回调的过程是一个串行同步的过程,这为我们保证了顺序,同时,需要开发人员注意的一点是,千万不要因为一个 Watcher 的处理逻辑影响了整个客户端的 Watcher 回调。

轻量

WatchedEvent 是 ZooKeeper 整个 Watcher 通知机制的最小通知单元,这个数据结构中只包含三部分内容:通知状态、事件类型和节点路径。也就是说,Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。例如针对 NodeDataChanged 事件,ZooKeeper 的 Watcher 只会通知客户端指定数据节点的数据内容发生了变更,而对于原始数据以及变更后的新数据都无法从这个事件中直接获取到,而是需要客户端主动重新去获取数据,这也是 ZooKeeper 的 Watcher 机制的一个非常重要的特性。

另外,客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象传递到服务端,仅仅只是在客户端请求中使用 boolean 类型属性进行了标记,同时服务端也仅仅只是保存了当前连接的 ServerCnxn 对象。

如此轻量的 Watcher 机制设计,在网络开销和服务端内存开销上都是非常廉价的。

注意事项

  1. 普通的 Watcher 是一次性触发器,如果你想未来还接收到通知则需要再注册一个 Watcher。
  2. 因为普通的 Watcher 是一次性触发器,并且在接收到通知和设置新 Watcher 都会有延时,在这段时间内节点的变更不会通知客户端。

ACL

权限:Permission

在ZooKeeper中,所有对数据的操作权限分为以下五大类:

· CREATE(C) :数据节点的创建权限,允许授权对象在该数据节点下创建子节点。

· DELETE(D) :子节点的删除权限,允许授权对象删除该数据节点的子节点。

· READ(R) :数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。

· WRITE(W) :数据节点的更新权限,允许授权对象对该数据节点进行更新操作。

· ADMIN(A) :数据节点的管理权限,允许授权对象对该数据节点进行 ACL 相关的设置操作。

权限模式:Scheme

权限模式用来确定权限验证过程中使用的检验策略。在ZooKeeper中,开发人员使用最多的就是以下四种权限模式。

  • IP:IP 模式通过 IP 地址粒度来进行权限控制,例如配置了“ip:192.168.0.110”,即表示权限控制都是针对这个 IP 地址的。同时,IP 模式也支持按照网段的方式进行配置,例如“ip:192.168.0.1/24”表示针对 192.168.0. 这个IP段进行权限控制。

  • Digest:Digest是最常用的权限控制模式,也更符合我们对于权限控制的认识,其以类似于“username:password”形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。当我们通过“username:password”形式配置了权限标识后,ZooKeeper 会对其先后进行两次编码处理,分别是 SHA-1 算法加密和 BASE64 编码。

  • World:World 是一种最开放的权限控制模式,从其名字中也可以看出,事实上这种权限控制方式几乎没有任何作用,数据节点的访问权限对所有用户开放,即所有用户都可以在不进行任何权限校验的情况下操作 ZooKeeper 上的数据。另外,World 模式也可以看作是一种特殊的 Digest 模式,它只有一个权限标识,即“world:anyone”。

  • Super:Super模式,顾名思义就是超级用户的意思,也是一种特殊的 Digest 模式。在Super模式下,超级用户可以对任意 ZooKeeper 上的数据节点进行任何操作。

总结

本文从数据模型、节点特性、版本、Watch 和 ACL 五个方面来介绍 Zookeeper 的系统模型。帮助大家了解 Zookeeper 的特性。

2MBdq.gif

参考文章

《从Paxos到Zookeeper》

zookeeper 官方网站