Zookeeper命令操作
Zookeeper数据模型
ZooKeeper 是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构。

Zookeeper这里面的每一个节点都被称为: ZNode,每个节点上都会保存自己的数据和节点信息。

节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下。
节点可以分为四大类:
- PERSISTENT 持久化节点
- EPHEMERAL 临时节点:-e
- PERSISTENT_SEQUENTIAL持久化顺序节点:-s
- EPHEMERAL_SEQUENTIAL 临时顺序节点:-es
Zookeeper服务端常用命令

-
查看服务
- ls / 查看目录
- ls /zookeeper 查看子节点
- ls -R / 查看所有
- ls / 查看目录
-
创建
- create /xxx
- create /xxx xxx 创建并且写入数据
- create /xxx/xxx 创建子集
- create /xxx
-
设置值
- set /xxx xxxx
- set /xxx xxxx
-
删除值
- delete /xxx
- deleteall /xxx 删除父级下的所有信息
- delete /xxx
-
临时节点
- 在会话结束后,会自动的删除掉。
- 添加 -e 表示临时节点
-
顺序节点
- 创建出来的节点,根据先后的顺序,会在节点之后带上一个数值,越往后值越大。
- create -s /xxx
-
创建临时顺序节点
- create -es /xxx
- create -es /xxx
-
查看详细信息
- ls / -s
- 身份标识
- cZxid = 0x0
- 节点的创建事务ID(ZooKeeper通过全局递增的事务ID追踪所有操作)。
- mZxid = 0x0
- 节点的最后修改事务ID (与
cZxid
相同,说明节点自创建后未被修改过)。
- 节点的最后修改事务ID (与
- cZxid = 0x0
- 时间戳
- ctime = Thu Jan 01 08:00:00 CST 1970
- 节点的创建时间 (Unix时间戳起点,即
epoch time
,可能表示未记录真实时间或示例占位值)。
- 节点的创建时间 (Unix时间戳起点,即
- mtime = Thu Jan 01 08:00:00 CST 1970
- 节点的最后修改时间(与创建时间相同,说明未被修改过)。
- ctime = Thu Jan 01 08:00:00 CST 1970
- 父子关系
- pZxid = 0x17
- 父节点的事务ID(
0x17
是父节点最后一次修改的事务ID)。
- 父节点的事务ID(
- pZxid = 0x17
- 版本控制
- cversion = 12
- 子节点的修改次数(该节点的子节点被更新过12次)。
- dataVersion = 0
- 节点数据的版本号(此处为0,说明节点未存储数据)。
- aclVersion = 0
- 权限控制列表的版本号(默认权限)。
- cversion = 12
- 其他属性
- ephemeralOwner = 0x0
- 临时节点的所有者会话ID(
0x0
表示非临时节点,是持久节点)。
- 临时节点的所有者会话ID(
- dataLength = 0
- 节点存储的数据长度(无数据)。
- numChildren = 4
- 当前节点的子节点数量(根目录下共有4个子节点)。
- ephemeralOwner = 0x0
- ls / -s
Zookeeper JavaAPI操作
Curator介绍
Curator是Netflix公司开源的一套zookeeper客户端框架,Curator是对Zookeeper支持最好的客户端框架。Curatoi封装了大部分Zookeeper的功能,比如Leader选举、分布式锁等,减少了技术人员在使用Zookeeper时的底层细节开发工作。
Curator框架主要解决了三类问题:
- 封装ZooKeeper Client与ZooKeeper Server之间的连接处理(提供连接重试机制等)。
- 提供了一套Fluent风格的AP!,并且在Java客户端原生API的基础上进行了增强(创捷多层节点、删除多层节点等)。
- 提供ZooKeeper各种应用场景(分布式锁、leader选举、共享计数器、分布式队列等)的抽象封装。
创建Maven项目
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dwl</groupId>
<artifactId>zk-client</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
</project>
java
package com.dwl;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* ZooKeeper Curator 客户端操作示例(含生产级最佳实践)
* <p>
* 核心设计原则:
* 1. 单例模式保证客户端唯一性(通过静态初始化块实现线程安全初始化)
* 2. 显式连接状态管理(监听连接状态变化)
* 3. 网络异常自动恢复机制(指数退避重试策略)
* 4. 资源生命周期管控(自动关闭连接池)
* <p>
* 关键配置说明:
* - 会话超时时间 > 3倍网络延迟 + 最大操作耗时(示例设置为15秒)
* - 重试策略采用指数退避算法(初始3秒,最多重试10次)
* - 连接字符串支持集群模式(示例为单节点)
*
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
public class CuratorTest {
/* 定义重试策略:指数退避算法
* 参数详解:
* - baseSleepTimeMs: 初始重试间隔(3秒)
* - maxRetries: 最大重试次数(10次)
* 退避规则:
* 3s → 6s → 12s → ... → 最终间隔 3 * 2^(10-1) = 1536s (~25分钟)
* 总重试时间上限:约25分钟(防止无限重试)
*/
static RetryPolicy policy = new ExponentialBackoffRetry(3000, 10);
// 单例客户端实例(线程安全初始化)
private static final CuratorFramework CLIENT;
static {
try {
// 构建客户端实例(推荐使用构建器模式)
CLIENT = CuratorFrameworkFactory.builder()
// 连接字符串(支持多节点集群:host1:port1,host2:port2)
.connectString("127.0.0.1:2181")
// 会话超时时间(关键参数)
// - 建议值:2~10分钟(根据业务容忍度调整)
// - 必须 > 连接超时时间,否则会触发会话过期
.sessionTimeoutMs(15_000)
// 连接超时时间(TCP握手超时)
// - 建议值:5~30秒(网络差时适当增大)
// - 必须 < 会话超时时间
.connectionTimeoutMs(10_000)
// 重试策略(指数退避)
.retryPolicy(policy)
// 构建客户端实例
.build();
// 注册连接状态监听器(处理网络闪断)
CLIENT.getConnectionStateListenable().addListener((c, newState) -> {
switch (newState) {
case SUSPENDED -> System.out.println("⚠️ 与 ZooKeeper 断开连接(可能网络抖动)");
case RECONNECTED -> System.out.println("✅ 重新连接成功(自动恢复中)");
case LOST -> System.err.println("❗️ 会话已丢失(需人工介入检查)");
}
});
// 启动客户端(单例初始化必须线程安全)
CLIENT.start();
// 阻塞等待初始连接(重要:防止未就绪时操作)
// 超时时间应根据实际部署环境调整
if (!CLIENT.blockUntilConnected(30, TimeUnit.SECONDS)) {
throw new RuntimeException("⚠️ ZooKeeper 连接超时,初始化失败");
}
} catch (Exception e) {
throw new RuntimeException("初始化 ZooKeeper 客户端失败", e);
}
}
// 优雅关闭客户端(释放连接池资源)
public static void shutdown() {
if (CLIENT != null) {
CLIENT.close();
System.out.println("✅ ZooKeeper 客户端已关闭");
}
}
// 全局访问入口(防御性编程)
public static CuratorFramework getClient() throws InterruptedException {
if (CLIENT == null) {
throw new IllegalStateException("ZooKeeper client 未初始化");
}
// 添加运行时状态检查(防御性编程)
checkClientHealth();
return CLIENT;
}
// 健康检查辅助方法
private static void checkClientHealth() throws InterruptedException {
boolean state = CLIENT.getZookeeperClient().blockUntilConnectedOrTimedOut();
if (!state) {
throw new IllegalStateException("❌ ZooKeeper 客户端处于不可用状态。");
}
}
public static void main(String[] args) throws Exception {
// 测试不同操作
// theFirstMethod();
// theSecondWay();
// groundlessCreateNode();
// createNodeData();
// createNodeSetType();
// getData();
// getDataSub();
// getNodeStatus();
// updateData();
// updateDataVersion();
// deleteData();
// deleteDataContainSub();
// mustBeDeleteSuccessfully();
executeAfterDeletion();
}
/**
* 第一种连接方式:基础连接模式
* <p>
* 特点说明:
* - 显式调用 start() 启动连接
* - 阻塞等待连接结果
* <p>
* 使用场景:
* - 简单脚本工具
* - 非生产环境测试
* <p>
* 注意事项:
* ❌ 不要在生产环境使用这种方式(缺乏优雅关闭机制)
*/
public static void theFirstMethod() throws InterruptedException {
// 显式启动客户端(非单例模式)
CuratorFramework client = CuratorFrameworkFactory.newClient(
"127.0.0.1:2181",
new ExponentialBackoffRetry(1000, 3)
);
client.start();
try {
if (client.blockUntilConnected(10, TimeUnit.SECONDS)) {
System.out.println("✅ 临时连接成功(非推荐方式)");
}
} finally {
client.close(); // 必须显式关闭
}
}
/**
* 第二种连接方式:构建器模式(推荐生产使用)
* <p>
* 核心优势:
* ✅ 支持命名空间隔离(相当于逻辑分组)
* ✅ 内置连接池管理(提升并发性能)
* ✅ 支持多种连接状态监听(实时感知网络状态)
* <p>
* 关键参数:
* - namespace:逻辑隔离空间(相当于独立ZooKeeper实例)
* - 自动重试机制:基于ExponentialBackoffRetry策略
* <p>
* 生命周期管理:
* 使用 try-with-resources 确保资源释放(自动调用close())
*/
public static void theSecondWay() {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60_000)
.connectionTimeoutMs(15_000)
.retryPolicy(policy)
.namespace("dev") // 逻辑隔离命名空间
.build();
try (CuratorFramework ignored = client) { // 自动关闭资源
client.start();
if (client.blockUntilConnected(10, TimeUnit.SECONDS)) {
System.out.println("✅ 开发环境连接成功");
// 执行CRUD操作...
}
} catch (Exception e) {
System.err.println("❌ 操作失败: " + e.getMessage());
}
}
/**
* 创建持久化节点(基础操作)
* <p>
* 节点类型说明:
* - PERSISTENT:持久节点(默认模式,会话结束后不删除)
* - EPHEMERAL:临时节点(会话结束时自动删除)
* - SEQUENCE:顺序节点(节点名自动追加单调递增序号)
* <p>
* 核心API说明:
* - creatingParentsIfNeeded():自动创建父节点(避免NoNodeException)
* - forPath(path):指定节点路径(支持绝对路径和相对路径)
* <p>
* 防御性编程:
* ✅ 操作前检查连接状态(通过blockUntilConnectedOrTimedOut())
* ✅ 捕获并处理特定异常(如NodeExistsException)
*/
public static void groundlessCreateNode() throws Exception {
CuratorFramework client = getClient();
// 创建节点没有指定数据,默认将客户端ip作为存储。
if (client.getZookeeperClient().blockUntilConnectedOrTimedOut()) {
String path = client.create()
.creatingParentsIfNeeded() // 自动创建父节点
.withMode(CreateMode.PERSISTENT) // 显式指定节点类型
.forPath("/dwl_node");
System.out.println("✅ 创建节点成功: " + path);
}
}
/**
* 创建带数据的节点
* <p>
* 数据操作规范:
* - 数据长度建议 < 1MB(ZooKeeper最佳实践)
* - 使用字节数组保证序列化一致性(避免字符串编码问题)
* <p>
* 注意事项:
* ❌ 避免存储敏感信息(可通过ACL控制访问)
* ❌ 注意数据版本控制(使用version参数实现乐观锁)
*/
public static void createNodeData() throws Exception {
CuratorFramework client = getClient();
if (client.getZookeeperClient().blockUntilConnectedOrTimedOut()) {
byte[] data = "config_data".getBytes(); // 使用UTF-8编码字节数组
String path = client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.forPath("/dwl_config", data);
System.out.println("✅ 创建带数据节点成功: " + path);
}
}
/**
* 创建临时节点(会话感知)
* <p>
* 典型应用场景:
* - 服务注册发现(临时节点自动注销)
* - 分布式锁(临时有序节点)
* <p>
* 特性说明:
* - 会话失效时自动删除(通过sessionTimeoutMs控制生命周期)
* - 不允许创建子节点(临时节点必须是叶子节点)
* <p>
* 最佳实践:
* ✅ 配合监听器实现服务健康监测(节点消失触发事件)
* ✅ 结合临时有序节点实现公平锁(顺序保证)
*/
public static void createNodeSetType() throws Exception {
CuratorFramework client = getClient();
if (client.getZookeeperClient().blockUntilConnectedOrTimedOut()) {
String path = client.create()
.withMode(CreateMode.EPHEMERAL) // 显式声明临时节点
.forPath("/dwl_ephemeral");
System.out.println("✅ 创建临时节点成功: " + path);
}
}
/**
* 获取节点数据
* <p>
* 数据操作规范:
* - 返回字节数组需按约定反序列化(示例中简单转为字符串)
* - 处理空节点场景(返回null时增加判空逻辑)
* <p>
* 异常处理:
* ❗️ 若节点不存在会抛出KeeperException$NoNodeException
* ❗️ 网络异常会抛出InterruptedException
*/
public static void getData() throws Exception {
CuratorFramework client = getClient();
// 获取原始字节数组(需根据业务场景反序列化)
byte[] data = client.getData().forPath("/case");
System.out.println("节点数据: " + new String(data));
}
/**
* 获取子节点列表
* <p>
* 数据操作规范:
* - 返回节点名称列表(不包含路径前缀)
* - 处理空目录场景(返回空列表)
* <p>
* 序列化建议:
* ✅ 对于复杂数据结构,推荐使用JSON/XML序列化
* ✅ 结合自定义Schema验证数据格式
*/
public static void getDataSub() throws Exception {
CuratorFramework client = getClient();
// 获取指定路径下的直接子节点(不包括递归子节点)
List<String> children = client.getChildren().forPath("/");
System.out.println("子节点列表: " + Arrays.toString(children.toArray()));
}
/**
* 获取节点元数据(Stat信息)
* <p>
* Stat结构说明:
* - czxid:创建事务ID
* - mzxid:最后修改事务ID
* - ctime:创建时间(毫秒)
* - mtime:最后修改时间(毫秒)
* - version:数据版本号
* - cversion:子节点版本号
* <p>
* 版本控制:
* ✅ 更新数据时需校验version参数(实现乐观锁)
* ✅ 删除节点时也需校验version参数
*/
public static void getNodeStatus() throws Exception {
CuratorFramework client = getClient();
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/case");
System.out.println("节点元数据:");
System.out.println("创建时间: " + stat.getCtime());
System.out.println("最后修改时间: " + stat.getMtime());
System.out.println("数据版本: " + stat.getVersion());
}
/**
* 更新节点数据(覆盖式写入)
* <p>
* 更新策略:
* - 直接替换现有数据(无版本校验)
* - 适用于可覆盖的非关键数据
*/
public static void updateData() throws Exception {
CuratorFramework client = getClient();
client.setData().forPath("/case", "case".getBytes());
getData();
}
/**
* 带版本控制的更新操作
* <p>
* 乐观锁实现:
* 1. 获取当前数据版本号
* 2. 提交更新时校验版本号
* 3. 版本不一致时抛出异常(需业务处理)
*/
public static void updateDataVersion() throws Exception {
CuratorFramework client = getClient();
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/case");
int version = stat.getVersion();
client.setData().withVersion(version).forPath("/case", "case_version".getBytes());
getData();
}
/**
* 删除单个节点(不包含子节点)
* <p>
* 删除规则:
* - 只有叶子节点可删除
* - 非空节点删除需设置deletingChildrenIfNeeded()
*/
public static void deleteData() throws Exception {
CuratorFramework client = getClient();
client.delete().forPath("/case_1");
getDataSub();
}
/**
* 递归删除节点及其子节点
* <p>
* 安全删除:
* ✅ 避免孤儿节点残留
* ✅ 重要节点删除需二次确认
*/
public static void deleteDataContainSub() throws Exception {
CuratorFramework client = getClient();
client.delete().deletingChildrenIfNeeded().forPath("/case_2");
getDataSub();
}
/**
* 强制删除保障操作
* <p>
* 保障机制:
* - guaranteed(): 服务端保证最终删除(即使网络闪断)
* - inBackground(): 异步执行回调(解耦操作)
*/
public static void mustBeDeleteSuccessfully() throws Exception {
CuratorFramework client = getClient();
client.delete().guaranteed().forPath("/app_10000000004");
getDataSub();
}
/**
* 异步删除回调处理
* <p>
* 异步场景:
* - 非关键路径删除操作
* - 需要与其他操作并行执行
* <p>
* 回调处理:
* ✅ 记录操作日志
* ✅ 发送通知事件
*/
public static void executeAfterDeletion() throws Exception {
CuratorFramework client = getClient();
client.delete().guaranteed().inBackground((CuratorFramework framework, CuratorEvent event) -> {
System.out.println("我被删除了");
System.out.println("事件类型: " + event.getType());
System.out.println("路径: " + event.getPath());
}).forPath("/app_10000000004"); // 阻塞等待异步完成(演示用)
getDataSub();
}
}
Watch事件监听
ZooKeeper 允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
ZooKeeper 中引入了Watcher机制来实现了发布/订阅功能能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态变化时,会通知所有订阅者。

zkCli客户端使用watch
添加 -w 参数可实时监听节点与子节点的变化,并且实时收到通知。非常适合保障分布式情况下的数据一致性。
其使用方式如下
- Is -w path
- 监听子节点的变化(增,删)[监听目录]
- get -w path
- 监听节点数据的变化
- stat -w path
- 监听节点属性的变化
Zookeeper事件类型
-
NodeCreated:节点创建
-
NodeDeleted:节点删除
-
NodeDataChanged:节点数据变化
-
NodeChildrenChanged:子节点列表变化
-
DataWatchRemoved:节点监听被移除
-
ChildWatchRemoved:子节点监听被移除
-
一次性监听,监听节点数据的变化。 get -w path
-
监听子节点的变化 Is -w path