互联网技术 / 互联网资讯 · 2024年1月5日

ZooKeeper 节点类型详解:不止 4 种

很多人在回答 ZooKeeper 节点类型时,习惯性地说只有 4 种:持久、持久顺序、临时、临时顺序。这个说法并不完整。以 ZooKeeper 3.6.2 为例,服务端实际支持 7 种节点类型。

这 7 种分别是:持久节点、持久顺序节点、临时节点、临时顺序节点、容器节点、持久 TTL 节点、持久顺序 TTL 节点。

下面按照特性和使用场景,系统梳理一下这些节点类型的区别,以及它们在服务端侧的大致实现方式。

ZooKeeper 的 7 种节点类型

1. 持久节点

持久节点是最常见、也是默认的节点类型。只要客户端不主动删除,它就会一直存在,不会因为会话结束而消失。

这类节点适合保存需要长期存在的配置信息、注册信息或业务状态。

2. 临时节点

临时节点和持久节点的主要区别在于生命周期。它会随着客户端会话结束而自动删除。

因此,临时节点常用于一些与连接状态强相关的场景,例如:

  • 服务实例存活探测
  • 健康检查
  • 分布式锁占用标记

3. 持久顺序节点

顺序节点的特点是:创建时,ZooKeeper 会在节点路径后自动追加一个递增的数字后缀。这样可以保证同一父节点下生成的路径具有唯一性。

持久顺序节点既保留了持久节点的特性,又增加了顺序编号,常见用途包括:

  • 分布式队列
  • 需要排序的任务列表
  • 有序事件记录

4. 临时顺序节点

临时顺序节点同时具备两个特点:会话结束后自动删除,以及自动追加顺序编号。

这类节点非常适合实现:

  • 分布式公平锁
  • 选主流程中的排队机制
  • 临时有序注册

5. 容器节点

容器节点是 ZooKeeper 3.5 之后新增的类型。创建时只需在 create 方法中指定 CreateMode 为 CONTAINER。

从使用表现来看,容器节点和持久节点很像,但它多了一个自动清理能力:服务端启动后,会有专门的后台线程定期扫描容器节点。当发现某个容器节点已经没有子节点时,就会自动删除该节点。

除了这个自动清理特性外,它与持久节点没有本质差别。

这种节点适合用于一些“父节点只是为了挂载子节点而存在”的场景,例如:

  • Leader 选举
  • 锁目录管理
  • 临时协调目录

6. 持久 TTL 节点

TTL 是 time to live 的缩写,表示存活时间。持久 TTL 节点本质上还是持久节点,但它带有超时控制能力。

当节点没有子节点,并且超过设定的 TTL 时间后,服务端会自动删除它。

它和容器节点很相似,区别在于:

  • 容器节点的删除条件主要是“没有子节点”
  • TTL 节点除了要求没有子节点外,还会受到 TTL 时间限制

需要注意的是,TTL 类型默认并不能直接使用,必须开启额外配置:

zookeeper.extendedTypesEnabled=true

如果没有开启该配置,创建 TTL 节点时通常会收到 Unimplemented 错误。

7. 持久顺序 TTL 节点

这种节点是在“持久顺序节点”的基础上,再叠加 TTL 能力。也就是说,它既会自动追加顺序编号,又支持在满足条件时按 TTL 自动清理。

适合需要“有序生成 + 自动过期回收”的场景。

顺序节点的实现原理

顺序节点的原理其实并不复杂。客户端在创建顺序节点时,服务端识别到该节点属于 sequential 类型,就会在原始路径后拼接一个固定宽度的数字后缀。

常见逻辑可以概括为:

如果节点是顺序节点,那么路径会变成:原始路径 + 格式化后的编号。

这个编号通常与父节点的版本信息相关,可以理解为当前父节点下子节点创建次数的递增序号。这样一来,生成出来的完整路径天然具备唯一性和有序性。

容器节点和 TTL 节点的清理机制

容器节点和 TTL 节点虽然使用方式不同,但在服务端侧的处理思路比较接近。

ZooKeeper 启动后,会额外启用一个后台定时任务线程,周期性扫描这两类节点,并根据节点状态决定是否删除。相关逻辑位于 ContainerManager 中,定时任务通过 TimerTask 执行。

扫描过程中,服务端会结合以下因素判断节点是否应被清理:

  • 节点是否还有子节点
  • TTL 是否已到期
  • 节点最后一次更新时间
  • 相关清理参数配置

相关配置项

与容器节点和 TTL 节点清理有关的常见配置包括:

  • znode.container.checkIntervalMs:默认 60000 毫秒,表示后台定时任务的检查间隔。
  • znode.container.maxPerMinute:默认 10000,和上面的配置共同决定扫描节奏。按默认值计算,两个节点之间的最小检查间隔大约为 6 毫秒。
  • znode.container.maxNeverUsedIntervalMs:默认 0。如果该值不为 0,那么当容器节点或 TTL 节点距离最后更新时间超过这个阈值时,也可能被删除。

如何理解这 7 种节点

如果把 ZooKeeper 节点类型拆成几个关键字来理解,会更容易记忆:

  • 持久:客户端不删除,就会一直存在。
  • 临时:会话断开后自动删除。
  • 顺序:服务端自动追加数字后缀。
  • 容器:服务端会定期扫描,没有子节点时可自动删除。
  • TTL:需要额外开启配置,在满足条件且超过生存时间后自动删除。

其中,TTL 节点还有一个值得注意的行为:当节点下再次创建子节点时,它的超时计时会被重置,因此它并不是简单地“到点必删”,而是与节点活跃度有关。

总结

ZooKeeper 的节点类型并不只是常见的 4 种。从较新的版本能力来看,完整范围应当包括 7 种:

  • 持久节点
  • 持久顺序节点
  • 临时节点
  • 临时顺序节点
  • 容器节点
  • 持久 TTL 节点
  • 持久顺序 TTL 节点

如果只是应对基础使用,前 4 种已经覆盖了大多数场景;但如果涉及自动清理、目录回收、过期控制等需求,那么容器节点和 TTL 节点就非常值得了解。

因此,再遇到“ZooKeeper 有几种节点类型”这个问题时,更准确的回答应该结合具体版本来说:至少在 3.6.2 中,服务端支持 7 种节点类型,而不是简单的 4 种。