ZooKeeper学习专栏(五):Java客户端开发(原生API)详解

文章目录

  • 前言
  • 一、核心类解析
    • 1.1 ZooKeeper类 - 连接管理核心
    • 1.2 Watcher接口 - 事件处理核心
  • 二、原生API实践
    • 2.1 创建会话(连接管理)
    • 2.2 创建节点(支持多种类型)
    • 2.3 获取节点数据和状态信息
    • 2.4 修改节点数据(版本控制)
    • 2.5 删除节点(版本控制)
    • 2.6 注册Watcher监听节点变化
    • 2.7 处理连接状态变化事件
  • 三、最佳实践与注意事项
  • 总结


前言

本文是Zookeeper第五个学习专栏,将深入探讨如何使用原生Java API进行Zookeeper客户端开发。通过详细的代码示例和注释,帮助开发者掌握核心API的使用方法


一、核心类解析

前置条件先引入Zookeeper客户端依赖,在Maven项目中添加以下依赖:

<dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.7.1</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions>
</dependency>

注意事项:
客户端版本应与服务端版本匹配。
建议排除冲突的日志依赖,使用项目统一的日志框架。

在ZooKeeper的Java客户端开发中,有两个核心类构成了整个API的基础框架:ZooKeeper类负责连接管理和基础操作,Watcher接口负责事件处理机制。下面我们将深入剖析这两个核心组件。

1.1 ZooKeeper类 - 连接管理核心

ZooKeeper类是客户端与ZooKeeper服务交互的主要入口,负责:

  • 建立和维护与ZooKeeper集群的连接。
  • 管理客户端会话生命周期。
  • 提供节点操作API(CRUD)。
  • 处理请求响应和序列化。

1. 构造方法:

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException

参数解析:

参数类型说明示例值
connectStringString集群连接字符串 格式:host1:port1,host2:port2“zk1:2181,zk2:2181,zk3:2181”
sessionTimeoutint会话超时时间(毫秒) 服务器端最小会话超时为tickTime*23000
watcherWatcher全局事件处理器 处理连接状态变化new MyWatcher()

2. 核心方法详解:
节点操作API

// 创建节点
String create(String path, byte[] data, List<ACL> acl, CreateMode createMode)// 删除节点
void delete(String path, int version)// 获取节点数据
byte[] getData(String path, boolean watch, Stat stat)// 设置节点数据
Stat setData(String path, byte[] data, int version)// 检查节点是否存在
Stat exists(String path, boolean watch)// 获取子节点列表
List<String> getChildren(String path, boolean watch)

连接管理

// 获取当前会话ID
long getSessionId()// 获取会话密码(用于重连)
byte[] getSessionPasswd()// 获取连接状态
States getState()// 关闭连接
void close()

4. 连接状态枚举(States)

public enum States {CONNECTING,     // 连接建立中ASSOCIATING,    // 关联中CONNECTED,      // 已连接CONNECTEDREADONLY, // 只读连接CLOSED,         // 已关闭AUTH_FAILED,    // 认证失败NOT_CONNECTED;  // 未连接
}

1.2 Watcher接口 - 事件处理核心

1. 接口定义与事件模型

public interface Watcher {void process(WatchedEvent event);
}

Watcher采用观察者模式,当ZooKeeper状态变化或节点变更时,会通过process()方法回调通知客户端。
2. WatchedEvent结构分析
WatchedEvent包含三个关键信息:

public class WatchedEvent {private final KeeperState keeperState; // 连接状态private final EventType eventType;     // 事件类型private final String path;            // 事件路径
}

3. 连接状态(KeeperState)

状态触发条件处理建议
SyncConnected成功连接到集群恢复正常操作
Disconnected与集群断开连接暂停写操作,尝试重连
Expired会话超时重建连接,恢复临时节点
AuthFailed认证失败检查ACL配置
ConnectedReadOnly连接到只读服务器避免写操作

4. 节点事件类型(EventType)

事件类型触发条件注册方式
NodeCreated节点被创建exists()
NodeDeleted节点被删除exists()/getData()
NodeDataChanged节点数据变更getData()
NodeChildrenChanged子节点变化getChildren()
DataWatchRemoved数据监视移除系统自动
ChildWatchRemoved子节点监视移除系统自动

5. Watcher特性深度解析
(1) 一次性触发机制
特性:Watcher在触发后会自动失效
影响:需要重新注册才能继续监听
解决方案

@Override
public void process(WatchedEvent event) {if (event.getType() == EventType.NodeDataChanged) {try {// 重新注册WatcherzooKeeper.getData(event.getPath(), this, null);} catch (Exception e) {// 处理异常}}
}

(2) 轻量级通知
特性:事件通知不包含具体变更内容
优势:减少网络传输开销
处理流程
轻量级通知
(3) 顺序保证
特性:客户端按事件发生的顺序接收通知
重要性:确保状态一致性
示例场景
节点数据变更(setData)
节点删除(delete)
客户端将按此顺序收到NodeDataChanged和NodeDeleted事件

(4) 会话事件优先级
特性:连接状态事件优先于节点事件
影响:当连接断开时,节点事件可能丢失
处理方案

public void process(WatchedEvent event) {// 优先处理连接状态事件if (event.getState() != KeeperState.SyncConnected) {handleSessionEvent(event.getState());return;}// 处理节点事件handleNodeEvent(event.getType(), event.getPath());
}

6. Watcher注册机制
下面给出三种注册方式:
构造方法注册:全局连接状态Watcher

ZooKeeper zk = new ZooKeeper(connectString, timeout, globalWatcher);

API调用注册:操作时指定Watcher

zk.getData("/node", specificWatcher, null);

默认Watcher:使用构造方法的Watcher

zk.exists("/node", true); // true表示使用默认Watcher

核心类协作流程:
协作流程

二、原生API实践

2.1 创建会话(连接管理)

public class ZookeeperConnector implements Watcher {private static final CountDownLatch connectedLatch = new CountDownLatch(1);private ZooKeeper zooKeeper;public ZooKeeper connect(String hosts, int timeout) throws Exception {zooKeeper = new ZooKeeper(hosts, timeout, this);connectedLatch.await(); // 等待连接建立return zooKeeper;}@Overridepublic void process(WatchedEvent event) {if (event.getState() == Event.KeeperState.SyncConnected) {connectedLatch.countDown(); // 连接建立时释放锁System.out.println("Successfully connected to ZooKeeper!");}}public static void main(String[] args) throws Exception {ZookeeperConnector connector = new ZookeeperConnector();ZooKeeper zk = connector.connect("localhost:2181", 3000);// 执行后续操作...zk.close();}
}

2.2 创建节点(支持多种类型)

// 创建持久节点
String persistentPath = zk.create("/test-persistent",        // 节点路径"persistent data".getBytes(), // 节点数据ZooDefs.Ids.OPEN_ACL_UNSAFE, // ACL权限控制CreateMode.PERSISTENT       // 节点类型
);
System.out.println("Created persistent node: " + persistentPath);// 创建临时顺序节点
String ephemeralPath = zk.create("/test-ephemeral-",        // 注意结尾的破折号"ephemeral data".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL // 临时顺序节点
);
System.out.println("Created ephemeral node: " + ephemeralPath);

2.3 获取节点数据和状态信息

// 获取节点数据(不注册Watcher)
byte[] data = zk.getData("/test-persistent", false, null);
System.out.println("Node data: " + new String(data));// 获取节点状态信息(Stat对象)
Stat stat = new Stat();
byte[] dataWithStat = zk.getData("/test-persistent", false, stat);// 输出节点状态信息
System.out.println("Version: " + stat.getVersion()); // 数据版本
System.out.println("Ctime: " + new Date(stat.getCtime())); // 创建时间
System.out.println("Mtime: " + new Date(stat.getMtime())); // 修改时间
System.out.println("Num children: " + stat.getNumChildren()); // 子节点数

2.4 修改节点数据(版本控制)

// 先获取当前版本
Stat currentStat = zk.exists("/test-persistent", false);
int currentVersion = currentStat.getVersion();// 更新数据(指定版本)
Stat newStat = zk.setData("/test-persistent","updated data".getBytes(),currentVersion // 指定版本确保原子操作
);
System.out.println("New version: " + newStat.getVersion());// 错误示例:使用过期版本
try {zk.setData("/test-persistent", "wrong data".getBytes(), currentVersion);
} catch (KeeperException.BadVersionException e) {System.err.println("Version conflict: " + e.getMessage());
}

2.5 删除节点(版本控制)

// 获取当前版本
Stat delStat = zk.exists("/test-to-delete", false);
if (delStat != null) {zk.delete("/test-to-delete", delStat.getVersion());System.out.println("Node deleted successfully");
}// 递归删除非空节点(原生API需自行实现递归)
deleteRecursive(zk, "/parent-node");private void deleteRecursive(ZooKeeper zk, String path) throws Exception {List<String> children = zk.getChildren(path, false);for (String child : children) {deleteRecursive(zk, path + "/" + child);}zk.delete(path, -1); // -1 忽略版本检查
}

2.6 注册Watcher监听节点变化

public class NodeWatcher implements Watcher {private final ZooKeeper zk;public NodeWatcher(ZooKeeper zk) {this.zk = zk;}@Overridepublic void process(WatchedEvent event) {try {if (event.getType() == Event.EventType.NodeDataChanged) {System.out.println("Node data changed: " + event.getPath());// 重新注册Watcher(Watcher是单次的)zk.getData(event.getPath(), this, null);} else if (event.getType() == Event.EventType.NodeChildrenChanged) {System.out.println("Node children changed: " + event.getPath());// 重新注册子节点Watcherzk.getChildren(event.getPath(), this);}} catch (Exception e) {e.printStackTrace();}}public void watchNode(String path) throws Exception {// 注册数据变更Watcherzk.getData(path, this, null);// 注册子节点变更Watcherzk.getChildren(path, this);}
}// 使用示例
NodeWatcher watcher = new NodeWatcher(zk);
watcher.watchNode("/test-watch");

2.7 处理连接状态变化事件

public class ConnectionWatcher implements Watcher {private ZooKeeper zk;private volatile boolean connected = false;private volatile boolean expired = false;public ZooKeeper connect(String hosts) throws Exception {zk = new ZooKeeper(hosts, 3000, this);while (!connected) {Thread.sleep(100);}return zk;}@Overridepublic void process(WatchedEvent event) {switch (event.getState()) {case SyncConnected:connected = true;System.out.println("Connected to ZooKeeper cluster");break;case Disconnected:connected = false;System.out.warn("Disconnected from ZooKeeper cluster");break;case Expired:expired = true;connected = false;System.err.println("Session expired. Need to reinitialize.");break;case AuthFailed:System.err.println("Authentication failed");break;}}public void close() throws InterruptedException {zk.close();}public boolean isConnected() {return connected;}public boolean isExpired() {return expired;}
}

三、最佳实践与注意事项

  1. 连接管理:
    • 使用CountDownLatch确保连接建立后再执行操作。
    • 实现自动重连机制处理Disconnected状态。
    • 会话过期后需要重建所有临时节点和Watcher。
  2. Watcher使用要点:
    • Watcher是单次触发的,事件处理后需重新注册。
    • 在连接断开期间发生的事件不会触发Watcher。
    • 避免在Watcher中进行长时间阻塞操作。
  3. 版本控制:
    • 使用版本号实现乐观锁控制
    • 在并发更新场景中必须处理BadVersionException
    • -1表示忽略版本检查(慎用)
  4. 异常处理:
try {// Zookeeper操作
} catch (KeeperException e) {switch (e.code()) {case NONODE:// 节点不存在处理break;case NODEEXISTS:// 节点已存在处理break;// 其他错误码处理...}
} catch (InterruptedException e) {Thread.currentThread().interrupt();
}

总结

本文系统介绍了使用ZooKeeper原生Java API进行客户端开发的核心技术:通过ZooKeeper类管理集群连接和会话生命周期,利用Watcher接口处理连接状态变化(SyncConnected/Disconnected/Expired)和节点事件(数据变更/子节点变化);详细演示了节点CRUD操作(含版本控制机制)、Watcher注册策略及一次性触发特性;强调连接管理的最佳实践(CountDownLatch同步、会话恢复)、异常处理方案(KeeperException错误码解析)和高效监听模式设计,为构建分布式协调服务提供坚实基础。

完整流程示意图:
完整流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91516.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

卸油管链接检测误报率↓76%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析&#xff0c;核心技术参数与架构设计引用自《陌讯技术白皮书》&#xff0c;禁止未经授权的转载与商用。一、行业痛点&#xff1a;卸油管链接检测的三大技术瓶颈在石化仓储与运输场景中&#xff0c;卸油管链接的密封性检测是保障安全生产的关键环节。…

MongoDB用户认证authSource

文章目录authSource遇到的问题authSource MongoDB用户认证逻辑与以往我认知的关系型数据库逻辑不太一样&#xff0c;多了一层用户与数据库关系的绑定。 在建立用户时&#xff0c;需要先指定数据库&#xff0c;则存在一个概念&#xff1a;用户归属于数据库。额外&#xff0c;依…

插件升级:Chat/Builder 合并,支持自定义 Agent、MCP、Rules

TRAE 插件全新升级&#xff0c;Chat、Builder 合并&#xff0c;支持自定义智能体、MCP 及自定义规则&#xff0c;体验对齐 IDE&#xff0c;现已上线 JetBrains 和 VSCode。 1. Chat/Builder 合并&#xff0c;一个对话框即可智能协作 在 TRAE 插件的 Chat 对话框中&#xff0…

【历史人物】【王安石】简历与生平

目录 一、王安石个人简历 二、个人主要经历 三、个人成就及影响 1、散文 2、诗歌 3、词 四、经典评价摘录 一、王安石个人简历 基本信息‌ 姓名&#xff1a;王安石&#xff0c;字介甫&#xff0c;号半山。小名獾郎 性别&#xff1a;男 年龄&#xff1a;1021年-1086年…

Codeforces Round 1040 (Div. 2) A - D题详细题解

本文为Codeforces Round 1040 (Div. 2) A - D题的详细题解, 觉得有帮助或者写的不错可以点个赞&#xff01; 目录 题目A: 题目大意: 解题思路: 代码(C): 题目B: 题目大意: 解题思路: 代码(C): 题目C: 题目大意: 解题思路: 代码(C): 题目D: 题目大意: 解题思路:…

数据结构 之 【排序】(计数排序)

目录 1.计数排序的思想 2.计数排序图解 3.计数排序代码逻辑 3.1求原数组最大最小值及计数数组的创建 3.2计数 3.3覆盖写 3.4释放资源 4.计数排序的注意事项 5.计数排序的时间复杂度与空间复杂度 以升序为例 1.计数排序的思想 前面我们学习的快排、归并排序、希尔排序.…

Ascend CANN/ACL API 模型部署加速最佳实践

1. 模型输入相关问题 图像尺寸信息 模型输入尺寸由原始模型决定,在转换时固定 图像尺寸信息是模型固有属性,不是转换时添加的 对于使用动态尺寸,可以在推理时自动根据当前的输入尺寸推导输出尺寸。 输入格式(NCHW/NHWC) --input_format 不同框架默认格式不同: Caffe: 支持…

QT信号和槽怎么传输自己定义的数据结构

在 Qt 中&#xff0c;信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;机制默认支持许多内置类型&#xff08;如 int、QString、QList 等&#xff09;&#xff0c;但如果要传输 自定义数据结构&#xff08;如结构体、类对象&#xff09;&#xff0c;需要额…

借助于llm将pdf转化为md文本

pdf转化为md格式后&#xff0c;意味着非结构化文本转为结构化文本&#xff0c;能清晰定位大标题、子标题&#xff0c;图表。 方便后续处理&#xff0c;因为llamaindex和langchain能更有效切分md类文本&#xff0c;避免信息丢失。 1&#xff09;读取pdf为txt 读取pdf&#xf…

设计模式:中介者模式 Mediator

目录前言问题解决方案结构代码前言 中介者是一种行为设计模式&#xff0c;能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互&#xff0c;迫使它们通过一个中介者对象进行合作。 问题 假如你有一个创建和修改客户资料的对话框&#xff0c; 它由各种控件…

计算机基础速通--数据结构·线性表应用

如有问题大概率是我的理解比较片面&#xff0c;欢迎评论区或者私信指正。 考察线性表&#xff0c;核心围绕其存储结构特性、核心操作实现、场景应用选型三大维度&#xff0c;重点检验对基础概念的理解、代码实现能力及问题分析能力&#xff0c;通常会结合算法设计、复杂度分析和…

LeetCode Hot 100:42. 接雨水

题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 解析 和题目 盛水最多的容器 类似&#xff0c; LeetCode Hot 100&#xff1a;11. 盛最多水的容器-CSDN博客 只是这里将每一个柱子视为一个宽度为…

【C语言入门级教学】字符指针变量

文章目录1.字符指针变量2. 数组指针变量2.1 数组指针变量初始化3.⼆维数组传参的本质1.字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* ; ⼀般使⽤: int main() { char ch w; char* pc &ch;//pc的类型是char**pcw;//对pc解引用 修改ch存放的内容…

【Shell脚本自动化编写——报警邮件,检查磁盘,web服务检测】

Shell脚本自动化编写Shell脚本自动化编写一、判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。第一步&#xff1a;准备工作第二步&#xff1a;配置邮件信息第三步&#xff1a;检查磁盘的自动…

Java 接口(下)

三、接口的继承性【基础重点】 1. Java中的接口之间的继承关系是多继承&#xff0c;一个接口可以有多个父接口(1) 语法&#xff1a;interface 接口名 extends 父接口1,父接口2{} 2. 类和接口之间是多实现的关系&#xff1a;一个类可以同时实现多个接口(1) 语法&#xff1a;clas…

学习游戏制作记录(各种水晶能力以及多晶体)8.1

1.实现创建水晶并且能与水晶进行交换位置的能力创建好水晶的预制体&#xff0c;添加动画控制器&#xff0c;传入待机和爆炸的动画创建Crystal_Skill_Control脚本&#xff1a;挂载在水晶预制体上private float crystalExstTime;//水晶存在时间public void SetupCrystal(float _c…

在vscode 如何运行a.nut 程序(Squirrel语言)

在 VS Code 中运行 Squirrel 语言编写的 .nut 程序&#xff0c;需要先配置 Squirrel 运行环境并安装相关插件&#xff0c;具体步骤如下&#xff1a; 一、安装 Squirrel 解释器 Squirrel 程序需要通过其官方解释器 squirrel 或 sq 执行&#xff0c;首先需要安装解释器&#xf…

【数据结构】生活中的数据结构:从吃饭与编程看栈与队列思维

生活中的数据结构&#xff1a;从吃饭与编程看栈与队列思维 在软件开发的世界里&#xff0c;栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;是两种基础的数据结构&#xff0c;它们以不同的顺序管理数据&#xff1a;栈遵循后进先出&#xff08;LIFO&#x…

牛客——接头密匙

题目链接&#xff1a;牛客--接头密匙 该题是一个很显然的前缀树问题&#xff0c;只需要构建a中所有数组对应的前缀树&#xff0c;之后求b所处前缀个数即可。关于前缀树的构建&#xff0c;可以观看左老师算法讲解045的视频&#xff0c;简单来讲就是用特殊字符将实际数据隔开&…

【Linux基础知识系列】第六十三篇 - 文件编辑器基础:vim

在 Linux 系统中&#xff0c;文本编辑器是系统管理员和开发人员不可或缺的工具。vim 是一个功能强大的文本编辑器&#xff0c;广泛应用于 Linux 系统中。它支持多种编辑模式&#xff0c;提供了丰富的文本编辑功能&#xff0c;适用于编写代码、配置文件和文档。掌握 vim 的基本使…