1. 引言
Apache ZooKeeper 是一个分布式的、开放源码的分布式应用程序协调服务,是 Hadoop 和 HBase 的重要组件。它主要用于解决分布式应用中经常遇到的一些数据管理问题,如统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
ZooKeeper 的数据模型采用类似文件系统的树状结构,每个节点称为一个 ZNode。ZNode 是 ZooKeeper 中数据存储的基本单元,可以存储数据,也可以有子节点。对 ZNode 的创建、删除、读取和更新操作是 ZooKeeper 客户端最核心的 API。
本文将重点介绍如何使用 ZooKeeper 的 Java API 进行节点的创建与删除操作,这是进行 ZooKeeper 应用开发的基础。
2. 环境准备
2.1 依赖引入
首先,需要在项目中引入 ZooKeeper 的 Java 客户端依赖。以 Maven 项目为例,在 pom.xml 中添加以下依赖:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.0</version>
</dependency>
http://my.tv.sohu.com/us/445385256/737670875.shtml
http://my.tv.sohu.com/us/445385256/737670954.shtml
http://my.tv.sohu.com/us/445385256/737670749.shtml
http://my.tv.sohu.com/us/445385256/737670911.shtml
http://my.tv.sohu.com/us/445385256/737670832.shtml
http://my.tv.sohu.com/us/445385256/737670382.shtml
http://my.tv.sohu.com/us/445385256/737670285.shtml
http://my.tv.sohu.com/us/445385256/737670360.shtml
http://my.tv.sohu.com/us/445385256/737670262.shtml
http://my.tv.sohu.com/us/445385256/737669895.shtml
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDg3NS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDk1NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDc0OS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDkxMS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDgzMi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDM4Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDI4NS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDM2MC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY3MDI2Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1Mzg1MjU2LzczNzY2OTg5NS5zaHRtbA==.html
,,,,
http://my.tv.sohu.com/us/370099978/737671903.shtml
http://my.tv.sohu.com/us/370099978/737671733.shtml
http://my.tv.sohu.com/us/370099978/737671823.shtml
http://my.tv.sohu.com/us/370099978/737671726.shtml
http://my.tv.sohu.com/us/370099978/737671718.shtml
http://my.tv.sohu.com/us/370099978/737671805.shtml
http://my.tv.sohu.com/us/370099978/737671400.shtml
http://my.tv.sohu.com/us/370099978/737671631.shtml
http://my.tv.sohu.com/us/370099978/737671702.shtml
http://my.tv.sohu.com/us/370099978/737671383.shtml
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTkwMy5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTczMy5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTgyMy5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTcyNi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTcxOC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTgwNS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTQwMC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTYzMS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTcwMi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvMzcwMDk5OTc4LzczNzY3MTM4My5zaHRtbA==.html
,,,,
http://my.tv.sohu.com/us/445135000/737672173.shtml
http://my.tv.sohu.com/us/445135000/737672342.shtml
http://my.tv.sohu.com/us/445135000/737672336.shtml
http://my.tv.sohu.com/us/445135000/737672333.shtml
http://my.tv.sohu.com/us/445135000/737672152.shtml
http://my.tv.sohu.com/us/445135000/737672239.shtml
http://my.tv.sohu.com/us/445135000/737672308.shtml
http://my.tv.sohu.com/us/445135000/737672132.shtml
http://my.tv.sohu.com/us/445135000/737672069.shtml
http://my.tv.sohu.com/us/445135000/737672221.shtml
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjE3My5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjM0Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjMzNi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjMzMy5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjE1Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjIzOS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjMwOC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjEzMi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjA2OS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQ1MTM1MDAwLzczNzY3MjIyMS5zaHRtbA==.html
2.2 启动 ZooKeeper 服务
在开发之前,需要确保有一个可用的 ZooKeeper 服务。可以通过以下方式之一启动:
- 单机模式:下载 ZooKeeper 发行版,解压后修改 conf/zoo.cfg 配置文件,然后运行 bin/zkServer.sh start(Linux/Mac)或 bin/zkServer.cmd(Windows)。
- Docker 方式:运行 docker run –name zookeeper -p 2181:2181 -d zookeeper:3.8。
默认情况下,ZooKeeper 服务会在 localhost:2181 端口监听。
3. 创建 ZooKeeper 客户端连接
在进行任何节点操作之前,首先需要建立与 ZooKeeper 服务器的连接。ZooKeeper 客户端通过 ZooKeeper 类来创建和管理连接。
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZkClientDemo {
private static final String CONNECT_STRING = "localhost:2181";
private static final int SESSION_TIMEOUT = 5000; // 会话超时时间,单位毫秒
public static ZooKeeper getConnection() throws IOException, InterruptedException {
CountDownLatch connectedLatch = new CountDownLatch(1);
// 创建 ZooKeeper 实例,异步连接
ZooKeeper zk = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
// 连接建立成功
connectedLatch.countDown();
}
}
});
// 等待连接建立完成
connectedLatch.await();
System.out.println("ZooKeeper 连接建立成功!");
return zk;
}
}
关键参数说明:
- connectString:ZooKeeper 服务器地址,格式为 host:port,多个地址用逗号分隔。
- sessionTimeout:会话超时时间,单位毫秒。客户端与服务器失去连接后,在此时间内重连成功可恢复会话。
- watcher:默认的 Watcher,用于接收连接状态变化等事件通知。
4. 创建节点(Create)
ZooKeeper 提供了 create 方法来创建节点。创建节点时需要指定路径、数据、ACL(访问控制列表)和节点类型。
4.1 节点类型
ZooKeeper 支持以下几种节点类型:
- 持久节点(PERSISTENT):创建后一直存在,直到被显式删除。
- 持久顺序节点(PERSISTENT_SEQUENTIAL):持久节点,但节点名后会附加一个单调递增的序号。
- 临时节点(EPHEMERAL):与客户端会话绑定,会话结束(连接断开)后节点自动删除。
- 临时顺序节点(EPHEMERAL_SEQUENTIAL):临时节点,节点名后附加序号。
4.2 创建持久节点示例
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import java.util.List;
public class ZkCreateDemo {
public static void createPersistentNode(ZooKeeper zk, String path, String data) throws Exception {
// 默认使用 OPEN_ACL_UNSAFE(所有人拥有所有权限)
List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE;
// 创建持久节点
String createdPath = zk.create(path,
data.getBytes(),
acl,
CreateMode.PERSISTENT);
System.out.println("持久节点创建成功,路径: " + createdPath);
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
// 创建 /demo 节点,数据为 "hello zookeeper"
createPersistentNode(zk, "/demo", "hello zookeeper");
zk.close();
}
}
4.3 创建临时节点示例
public class ZkCreateEphemeralDemo {
public static void createEphemeralNode(ZooKeeper zk, String path, String data) throws Exception {
String createdPath = zk.create(path,
data.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
System.out.println("临时节点创建成功,路径: " + createdPath);
// 临时节点会在会话结束后自动删除
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
createEphemeralNode(zk, "/tempNode", "临时数据");
// 保持连接,观察节点存在
Thread.sleep(10000);
zk.close(); // 关闭连接后,临时节点会被自动删除
}
}
4.4 创建顺序节点示例
public class ZkCreateSequentialDemo {
public static void createSequentialNode(ZooKeeper zk, String path, String data) throws Exception {
// 创建持久顺序节点
String createdPath = zk.create(path,
data.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println("顺序节点创建成功,实际路径: " + createdPath);
// 如果 path 是 "/seq-",实际创建的路径可能是 "/seq-0000000001"
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
// 连续创建三个顺序节点
createSequentialNode(zk, "/seq-", "data1");
createSequentialNode(zk, "/seq-", "data2");
createSequentialNode(zk, "/seq-", "data3");
zk.close();
}
}
5. 删除节点(Delete)
ZooKeeper 提供了 delete 方法来删除节点。删除节点时需要注意:
5.1 简单删除节点
import org.apache.zookeeper.KeeperException;
public class ZkDeleteDemo {
public static void deleteNode(ZooKeeper zk, String path) throws Exception {
try {
// version = -1 表示忽略版本号,直接删除
zk.delete(path, -1);
System.out.println("节点删除成功: " + path);
} catch (KeeperException.NoNodeException e) {
System.out.println("节点不存在: " + path);
} catch (KeeperException.NotEmptyException e) {
System.out.println("节点 " + path + " 下存在子节点,无法删除");
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
// 先创建一个测试节点
zk.create("/testDelete", "to be deleted".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 删除该节点
deleteNode(zk, "/testDelete");
zk.close();
}
}
5.2 带版本号的删除(CAS)
ZooKeeper 中每个节点都有一个数据版本号(version),每次数据更新版本号都会递增。删除时可以指定版本号,只有当前版本号匹配时才会执行删除,这实现了乐观锁机制。
public class ZkDeleteWithVersionDemo {
public static void deleteNodeWithVersion(ZooKeeper zk, String path, int expectedVersion) throws Exception {
try {
zk.delete(path, expectedVersion);
System.out.println("节点删除成功: " + path + " (版本: " + expectedVersion + ")");
} catch (KeeperException.BadVersionException e) {
System.out.println("版本不匹配,删除失败。当前版本可能已被其他客户端修改。");
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
// 创建节点并获取其当前状态(包含版本号)
zk.create("/casNode", "initial".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 获取节点状态,得到当前版本号
org.apache.zookeeper.data.Stat stat = zk.exists("/casNode", false);
int currentVersion = stat.getVersion();
System.out.println("当前节点版本: " + currentVersion);
// 使用正确版本号删除(应该成功)
deleteNodeWithVersion(zk, "/casNode", currentVersion);
// 尝试使用错误版本号删除(应该失败)
// deleteNodeWithVersion(zk, "/casNode", currentVersion); // 节点已不存在
zk.close();
}
}
5.3 递归删除非空节点
ZooKeeper 原生的 delete 方法不能删除含有子节点的节点。如果需要删除整个子树,需要先递归删除所有子节点。
import java.util.List;
public class ZkDeleteRecursiveDemo {
public static void deleteRecursive(ZooKeeper zk, String path) throws Exception {
// 获取子节点列表
List<String> children = zk.getChildren(path, false);
for (String child : children) {
String childPath = path.equals("/") ? "/" + child : path + "/" + child;
deleteRecursive(zk, childPath); // 递归删除子节点
}
// 删除当前节点
try {
zk.delete(path, -1);
System.out.println("删除节点: " + path);
} catch (KeeperException.NoNodeException e) {
// 节点可能已被删除,忽略
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZkClientDemo.getConnection();
// 创建测试树结构
zk.create("/parent", "parent data".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/parent/child1", "child1".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/parent/child2", "child2".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/parent/child1/grandchild", "grand".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 递归删除整个 /parent 子树
deleteRecursive(zk, "/parent");
zk.close();
}
}
6. 完整示例:节点管理工具类
下面是一个整合了创建和删除功能的工具类,包含异常处理和连接管理。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ZkNodeManager {
private ZooKeeper zk;
private final String connectString;
private final int sessionTimeout;
public ZkNodeManager(String connectString, int sessionTimeout) {
this.connectString = connectString;
this.sessionTimeout = sessionTimeout;
}
// 建立连接
public void connect() throws IOException, InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
this.zk = new ZooKeeper(connectString, sessionTimeout, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
latch.countDown();
}
});
latch.await();
System.out.println("Connected to ZooKeeper: " + connectString);
}
// 关闭连接
public void close() throws InterruptedException {
if (zk != null) {
zk.close();
System.out.println("ZooKeeper connection closed.");
}
}
// 创建节点
public String createNode(String path, String data, CreateMode mode) throws KeeperException, InterruptedException {
return createNode(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, mode);
}
public String createNode(String path, byte[] data, List<ACL> acl, CreateMode mode)
throws KeeperException, InterruptedException {
return zk.create(path, data, acl, mode);
}
// 删除节点
public boolean deleteNode(String path) throws KeeperException, InterruptedException {
return deleteNode(path, -1);
}
public boolean deleteNode(String path, int version) throws KeeperException, InterruptedException {
try {
zk.delete(path, version);
return true;
} catch (KeeperException.NoNodeException e) {
System.out.println("Node does not exist: " + path);
return false;
} catch (KeeperException.NotEmptyException e) {
System.out.println("Node has children, cannot delete: " + path);
return false;
} catch (KeeperException.BadVersionException e) {
System.out.println("Version mismatch for node: " + path);
return false;
}
}
// 递归删除节点及其子节点
public void deleteRecursive(String path) throws KeeperException, InterruptedException {
List<String> children = zk.getChildren(path, false);
for (String child : children) {
String childPath = path.equals("/") ? "/" + child : path + "/" + child;
deleteRecursive(childPath);
}
deleteNode(path);
}
// 检查节点是否存在
public boolean exists(String path) throws KeeperException, InterruptedException {
Stat stat = zk.exists(path, false);
return stat != null;
}
// 使用示例
public static void main(String[] args) throws Exception {
ZkNodeManager manager = new ZkNodeManager("localhost:2181", 5000);
manager.connect();
// 创建持久节点
String nodePath = manager.createNode("/demoRoot", "root data", CreateMode.PERSISTENT);
System.out.println("Created: " + nodePath);
// 创建子节点
manager.createNode("/demoRoot/child1", "child data", CreateMode.PERSISTENT);
// 检查节点是否存在
System.out.println("/demoRoot exists: " + manager.exists("/demoRoot"));
// 删除子节点
boolean deleted = manager.deleteNode("/demoRoot/child1");
System.out.println("Child deleted: " + deleted);
// 递归删除父节点
manager.deleteRecursive("/demoRoot");
manager.close();
}
}
网硕互联帮助中心


评论前必须登录!
注册