乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • ZooKeeper 学习路径
  • 第1章 分布式协调与ZooKeeper概述

    • ZooKeeper 概述
  • 第2章 单机与集群搭建

    • 配置参数详解
    • 集群搭建
  • 第3章 数据模型与ZNode

    • ZNode 详解
    • 节点类型对比
    • 顺序节点
    • ACL 权限控制
  • 第4章 会话与Watcher机制

    • 会话机制
    • Watcher 机制
  • 第5章 ZAB协议与一致性保证

    • ZAB 协议
    • 一致性保证
    • 数据同步
  • 第6章 Leader选举

    • Leader 选举
  • 第7章 典型应用:分布式锁

    • 分布式锁
  • 第8章 典型应用:配置中心与命名服务

    • 配置中心
    • 命名服务
  • 第9章 客户端编程基础(Java原生API)

    • Java 原生 API 编程
  • 第10章 运维与监控

    • 四字命令
    • 监控体系
  • 第11章 面试考点与设计思想

    • 设计思想
    • 面试考点汇编

本章定位:全面对比 ZooKeeper 的四类节点组合类型,理解各自的创建方式、生命周期和典型应用场景。

定义与作用

ZooKeeper 提供 4 种节点类型,由两个维度组合而成:

维度选项含义
持久性Persistent / Ephemeral断开连接后节点是否保留
顺序性普通 / Sequential是否自动追加 10 位递增编号

组合后得到 4 种类型:

Persistent(持久)   Persistent + Sequential(持久顺序)
Ephemeral(临时)    Ephemeral + Sequential(临时顺序)

ZooKeeper 3.5.3 后还引入了 Container 节点和 TTL 节点,前者在最后一个子节点删除后自动删除,后者基于 TTL 过期自动删除。完整节点类型共 6 种:Persistent、Persistent Sequential、Ephemeral、Ephemeral Sequential、Container、TTL。

核心原理

各类型生命周期

临时节点在 Session 结束时由服务端自动删除,不会留下"孤儿"节点。临时节点不能拥有子节点(Container 和 TTL 节点除外)。

类型特性对比

特性PersistentEphemeralPersistent SequentialEphemeral Sequential
Session 断开后保留删除保留删除
可否有子节点可以不可以可以不可以
名称格式用户定义用户定义用户定义 + 10 位序号用户定义 + 10 位序号
典型场景配置存储服务注册全局唯一 ID分布式锁
创建命令create /path datacreate -e /path datacreate -s /path datacreate -e -s /path data

Container 节点(扩展类型)

Container 节点是 ZooKeeper 3.5.3 引入的持久节点变体,核心语义是当最后一个子节点被删除时,容器节点自动被清理。它解决了"递归创建目录结构后需手动逐层清理"的运维痛点。

启用方式:需在 zoo.cfg 中设置 zookeeper.extendedTypesEnabled=true(3.5.3~3.5.x 需要,3.6.0+ 默认开启)。

创建命令:

create -c /containers/my-container "container-data"
# Created /containers/my-container

自动清理机制:

Container 节点的清理由 Leader 异步执行,不是即时删除,存在短暂的时间窗口。Leader 每 cnxTimeout 左右执行一次容器清理任务。

重要特性:

特性说明
子节点限制可以有子节点
Session 退出后保留(属于持久类)
清理触发最后一个子节点被删除后,容器在下次清理周期中被删除
清理范围不递归——只清理到孙子层为空的 Container 节点,不向更深层传播
实现类ContainerManager,Leader 端运行
删除接口超时如果创建子节点时 Container 已经不存在,服务端返回 NoNodeException,客户端需自行 exists() 检测并重建

清理不递归的含义:

create -c /grandparent            # 容器 A
create /grandparent/parent        # 持久节点 B
create -c /grandparent/parent/leaf # 容器 C

delete /grandparent/parent/leaf   # 容器 C 被清理
# /grandparent/parent 不会被自动删除(它是持久节点)
# /grandparent 有子节点 parent,不会触发清理

TTL 节点(扩展类型)

TTL 节点创建时指定一个存活时间(毫秒),到期后由服务端自动删除。适用于"临时状态但希望跨 Session 保留一段时间"的场景——例如某个请求的中间结果,在请求处理完成后一定时间内自动回收。

启用方式:需在 zoo.cfg 中设置 zookeeper.extendedTypesEnabled=true,并配置 zookeeper.ttlEnabled=true(3.6.0+)。默认 TTL 过期检查间隔由 zookeeper.ttlIntervalInSeconds 控制(默认 300 秒,即 5 分钟)。

创建命令:

# TTL 值以毫秒为单位
create -t 5000 /ttl-nodes/cache "cached-data"
# Created /ttl-nodes/cache (约 5 秒后自动删除)

TTL 编码规则:TTL 值以 8 字节(Big-Endian long)存储在节点的 stat 结构中,其值为 System.currentTimeMillis() + ttl(创建时刻 + 存活时间),即一个绝对到期时间戳而非相对时长。

过期清理机制:

机制说明
检查触发TTLNodeDeletePolicy 在服务端定期扫描
扫描范围全局 TTL 节点——不区分 create -t 和 setData + stat.ttl
删除时机System.currentTimeMillis() > stat.ttl 时删除
子节点处理TTL 节点的子节点(若有)在父节点过期删除时成为孤儿(ZooKeeper 不会级联删除子节点),需避免给 TTL 节点创建子节点

TTL 节点与临时节点的区别:

维度临时节点TTL 节点
生命周期绑定 Session绑定到期时间
Session 重连节点保留与 Session 无关,不受影响
能否跨 Session否是
典型场景服务在线性(注册中心)带有效期缓存(短时中间结果)

完整示例

示例一:Ephemeral 节点生命周期验证

场景说明:模拟微服务注册场景,服务启动注册临时节点,停止后自动清除。

操作前状态:ZooKeeper 集群正常,无临时节点。

# Client A —— 连接到 ZooKeeper
zkCli.sh -server 127.0.0.1:2181
[zkshell: 0] create -e /services/order-service-1 "http://192.168.1.10:8080"
Created /services/order-service-1

[zkshell: 1] ls /services
[order-service-1]
# Client B —— 验证可见性
zkCli.sh -server 127.0.0.1:2181
[zkshell: 0] ls /services
[order-service-1]

模拟服务宕机(关闭 Client A 终端):

# Client B 再次查看
[zkshell: 1] ls /services
[]
# 临时节点 order-service-1 已被 ZooKeeper 自动删除

操作后状态:

时间点/services 子节点说明
Client A 创建[order-service-1]服务在线
Client A 断开 10 秒后[]Session 过期,节点自动删除

示例二:Sequential 节点序号生成

场景说明:使用 Sequential 节点实现分布式唯一 ID 生成。

# 多个客户端同时创建
# Client A:
[zkshell: 0] create -s /ids/req- "request-data"
Created /ids/req-0000000001

# Client B(几乎同时):
[zkshell: 0] create -s /ids/req- "another-request"
Created /ids/req-0000000002

# Client C:
[zkshell: 0] create -s /ids/req- "third-request"
Created /ids/req-0000000003

操作前后对比:

维度操作前操作后
/ids 子节点空req-0000000001 ~ req-0000000003
序号保证—单调递增,全局唯一
并发安全—由 Leader 原子分配

序号由父节点维护的计数器生成,范围为 int(4 字节),溢出后会变为负数(req--2147483648)。实际使用中这个范围绰绰有余。

示例三:Ephemeral Sequential 实现分布式锁(最小序号策略)

场景说明:电商秒杀服务中多个实例竞争锁。

// 每个客户端创建一个临时顺序节点
String lockPath = zk.create("/lock/req-", data,
        ZooDefs.Ids.OPEN_ACL_UNSAFE,
        CreateMode.EPHEMERAL_SEQUENTIAL);

// 获取 /lock 下所有子节点并排序
List<String> children = zk.getChildren("/lock", false);
Collections.sort(children);

// 判断是否是最小序号
String myNode = lockPath.substring("/lock/".length());
if (children.get(0).equals(myNode)) {
    System.out.println("我获取了锁: " + lockPath);
} else {
    // 监听前一个节点的删除事件
    int myIndex = children.indexOf(myNode);
    String prevNode = children.get(myIndex - 1);
    zk.exists("/lock/" + prevNode, event -> {
        System.out.println("前一个节点释放,我获取锁");
    });
}

操作前后对比:

状态描述
操作前3 个客户端各自创建节点:req-001, req-002, req-003
竞争结果持有 req-001 的客户端获取锁
req-001 释放后req-002 客户端收到通知,获取锁

示例四:Container 节点自动清理验证

场景说明:使用 Container 节点管理一批中间计算结果,当所有子结果被消费后容器自动清理。

操作前状态:

[zkshell: 0] ls /
[zookeeper]
[zkshell: 1] create -c /results ""
Created /results

操作过程:

# 创建子节点存放中间结果
[zkshell: 2] create /results/step1 "intermediate-data-1"
Created /results/step1
[zkshell: 3] create /results/step2 "intermediate-data-2"
Created /results/step2
[zkshell: 4] ls /results
[step1, step2]

# 消费完成后逐个子节点删除
[zkshell: 5] delete /results/step1
[zkshell: 6] delete /results/step2

# 等待 Leader 清理周期后
[zkshell: 7] ls /results
# KeeperErrorCode = NoNode for /results
# 容器 /results 已被自动删除

操作前后对比:

时间点/results 状态说明
初始不存在—
create -c 后存在,空容器等待子节点
子节点存在时存在,含 2 个子节点正常工作
所有子节点删除后不存在(自动清理)无需手动 delete /results

示例五:TTL 节点创建与过期

场景说明:缓存一条短时有效的通知消息,5 秒后自动失效。

操作前状态:

[zkshell: 0] ls /notifications
Node does not exist: /notifications

操作过程:

# TTL = 5000 毫秒(5 秒)
[zkshell: 1] create /notifications ""
Created /notifications

[zkshell: 2] create -t 5000 /notifications/msg-01 "maintenance alert"
Created /notifications/msg-01

# 5 秒内查看
[zkshell: 3] get /notifications/msg-01
maintenance alert

# 等待 5 秒后(依赖 TTL 检查周期)
[zkshell: 4] get /notifications/msg-01
# KeeperErrorCode = NoNode for /notifications/msg-01

操作前后对比:

时刻/notifications/msg-01说明
创建时存在,data=maintenance alertTTL 计时开始
5 秒内存在可正常读写
TTL 到期后被删除无需手动清理

易错场景与面试考点

易错场景

1. 给 Ephemeral 节点创建子节点

create -e /ephemeral-node "data"
# Created /ephemeral-node

create /ephemeral-node/child "child"
# KeeperErrorCode = NoChildrenForEphemerals

临时节点不允许拥有子节点,这是 ZooKeeper 的硬约束。

2. Ephemeral 节点在 Session 迁移期间的状态

如果客户端断开后在同一次 Session 超时前重新连接到另一个 Server,Ephemeral 节点不会丢失。只有当 Session 真正过期(超过 timeout)时,Ephemeral 节点才会被删除。

3. Sequential 计数器溢出

计数器是有符号 int,最大值 2147483647。溢出后变为 -2147483648,节点名格式变为 prefix--2147483648。虽然极少发生,但在极端高频场景下应知晓此行为。

4. Container 节点子节点并发删除的竞态

// 客户端 A:删除最后一个子节点
zk.delete("/container/last-child", -1);

// 客户端 B:几乎同时尝试在同一个 Container 下创建新子节点
zk.create("/container/new-child", data, ...);
// 可能收到 NoNodeException:因为 Container 已在清理周期中被删除

应对方案:创建前先 exists() 检查,若 Container 被删则重新 create -c 重建容器。

5. TTL 节点在服务器时间回拨时的异常行为

TTL 到期时间戳 System.currentTimeMillis() + ttl 是绝对时间。如果服务器发生 NTP 时钟同步导致时间回拨(例如回拨 10 分钟),已到期的 TTL 节点可能"复活"——原先判定为过期的节点,新的 currentTimeMillis() 小于到期时间戳,节点重新变为"未过期"。生产环境务必确保 ZooKeeper 服务器的 NTP 同步稳定。

面试高频题

Q:ZNode 到底有几种类型?(高频陷阱)

A:标准回答是 6 种:

  • 4 种持久类型:Persistent、Persistent Sequential、Ephemeral、Ephemeral Sequential
  • 2 种扩展类型(3.5.3+):Container、TTL

如果只回答 4 种会丢分,面试官可能追问"知道 Container 节点和 TTL 节点吗?"。同时需要说明 Container 在最后一个子节点删除后自动清理、TTL 基于绝对到期时间自动删除的实现区别。

Q:Container 节点和 Persistent 节点的区别?

A:两者都是持久节点(Session 断开后保留),但 Container 在最后一个子节点删除后会被 Leader 异步清理,而普通 Persistent 节点永远保留直到显式删除。Container 适合"一批中间结果的临时目录"场景。

Q:Ephemeral 节点和 Persistent 节点的核心区别?

A:三个维度:

  1. 生命周期:Ephemeral 绑定 Session,Session 结束自动删除;Persistent 永久存在
  2. 子节点:Ephemeral 不能有子节点,Persistent 可以
  3. 使用场景:Ephemeral 表达"存活"语义(服务注册、分布式锁),Persistent 表达"持久状态"(配置中心)

Q:为什么 Ephemeral 节点不能有子节点?

A:保证删除语义的简单性。如果 Ephemeral 可以有子节点,Session 过期时需要递归删除整棵子树,增加了实现复杂度和删除过程中的不一致窗口。

Q:Sequential 节点在分布式锁中的作用?

A:多个竞争者各自创建 EPHEMERAL_SEQUENTIAL 节点,序号最小的获取锁。其他竞争者 Watch 前一个节点,形成公平的排队机制,避免惊群效应(Herd Effect)。

小结

要点说明
4 种组合类型Persistent / Ephemeral × 普通 / Sequential
Ephemeral绑定 Session,适合服务注册
Sequential自动递增编号,适合分布式锁和 ID 生成
Ephemeral+Sequential分布式锁的最佳组合
Ephemeral 不可有子节点硬约束
Container/TTL3.5.3+ 引入,增强自动清理能力

理解了节点类型,接下来深入 ACL 权限控制,学习如何保护 ZNode 的安全性。

上一页
ZNode 详解
下一页
顺序节点