This is article 30 in the Big Data series. Demonstrates how to operate ZooKeeper using Java code via ZkClient library, including establishing connection, node CRUD, child node monitoring, and data change monitoring.
Complete illustrated version: CSDN Original | Juejin
Why Use ZkClient
ZooKeeper official Java client (org.apache.zookeeper.ZooKeeper) has relatively low-level API, requires manually handling Session reconnection, Watcher re-registration, and other details. ZkClient is a high-level wrapper of the official client, providing:
- Automatic reconnection and Session recovery
- Persistent Watcher (internally auto re-registers)
- Synchronous API, reduces callback nesting
Maven Dependencies
<dependencies>
<!-- ZooKeeper core library, version should match server -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.4</version>
</dependency>
<!-- ZkClient high-level wrapper -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
</dependencies>
Establish Session Connection
import org.I0Itec.zkclient.ZkClient;
public class ZkDemo {
// Connection string: multiple nodes separated by comma, client will auto load balance
private static final String ZK_SERVERS = "h121.wzk.icu:2181,h122.wzk.icu:2181,h123.wzk.icu:2181";
public static void main(String[] args) throws InterruptedException {
ZkClient zkClient = new ZkClient(ZK_SERVERS);
System.out.println("ZooKeeper session created.");
// Keep main thread running, wait for async notifications
Thread.sleep(Long.MAX_VALUE);
}
}
ZkClient constructor blocks until connection is established.
Node Operations
Create Persistent Node (Recursive)
// Second parameter true means recursive create parent directories (like mkdir -p)
// If /wzk-java doesn't exist, will auto create
zkClient.createPersistent("/wzk-java/temp", true);
System.out.println("ZooKeeper create ZNode: /wzk-java/temp");
Equivalent zkCli command:
create -p /wzk-java ""
create /wzk-java/temp ""
Create Ephemeral Node
// Ephemeral node: auto deleted after Session disconnects, cannot have child nodes
zkClient.createEphemeral("/wzk-java/session-lock");
System.out.println("Ephemeral node created.");
Write Data
// Write when node exists, error if doesn't exist (need createPersistent first)
zkClient.writeData("/wzk-java/temp", "hello-zookeeper");
Read Data
String data = zkClient.readData("/wzk-java/temp");
System.out.println("Node data: " + data);
Delete Node (Recursive)
// deleteRecursive deletes node and all child nodes recursively
zkClient.deleteRecursive("/wzk-java");
System.out.println("ZooKeeper delete recursive: /wzk-java");
Equivalent zkCli command:
deleteall /wzk-java
Monitoring: Child Node Changes
subscribeChildChanges monitors child node list add/delete changes under specified path, doesn’t monitor target node’s own data changes.
// First create parent node
zkClient.createPersistent("/wzk-data", true);
zkClient.subscribeChildChanges("/wzk-data", new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) {
System.out.println("子节点变化!");
System.out.println(" 父节点路径: " + parentPath);
System.out.println(" 当前子节点列表: " + currentChilds);
}
});
System.out.println("已注册子节点监听,等待变更...");
Trigger test (zkCli):
# Create child node → trigger notification
create /wzk-data/child-1 "data1"
# Create another one
create /wzk-data/child-2 "data2"
# Delete child node → trigger again
delete /wzk-data/child-1
Output example:
子节点变化!
父节点路径: /wzk-data
当前子节点列表: [child-1]
子节点变化!
父节点路径: /wzk-data
当前子节点列表: [child-1, child-2]
子节点变化!
父节点路径: /wzk-data
当前子节点列表: [child-2]
ZkClient’s
subscribeChildChangesinternally auto re-registers after each notification, no manual handling needed.
Monitoring: Node Data Changes
subscribeDataChanges monitors specified node’s data modification and node deletion events.
Since ZkClient default serializer cannot deserialize ordinary strings, need to switch to SerializableSerializer (or custom serializer):
import org.I0Itec.zkclient.serialize.SerializableSerializer;
// Switch serializer (String implements Serializable, can use directly)
zkClient.setZkSerializer(new SerializableSerializer());
// First write initial data
zkClient.createPersistent("/wzk-data/test-data", true);
zkClient.writeData("/wzk-data/test-data", "initial-value");
// Register data change monitoring
zkClient.subscribeDataChanges("/wzk-data/test-data", new IZkDataListener() {
@Override
public void handleDataChange(String dataPath, Object data) {
System.out.println("数据改变: " + dataPath + " → " + data);
}
@Override
public void handleDataDeleted(String dataPath) {
System.out.println("节点被删除: " + dataPath);
}
});
System.out.println("已注册数据监听,等待变更...");
Trigger test (zkCli):
# Modify data → trigger handleDataChange
set /wzk-data/test-data "updated-value"
# Modify again
set /wzk-data/test-data "final-value"
# Delete node → trigger handleDataDeleted
delete /wzk-data/test-data
Output example:
数据改变: /wzk-data/test-data → updated-value
数据改变: /wzk-data/test-data → final-value
节点被删除: /wzk-data/test-data
ZkClient vs Native API Comparison
| Capability | Native ZooKeeper API | ZkClient |
|---|---|---|
| Watcher Re-registration | Manual, need re-call after each trigger | Auto, internal persistent monitoring |
| Session Reconnection | Need implement reconnection logic yourself | Auto reconnection |
| Exception Handling | Need handle KeeperException | Wrapped as ZkException |
| API Style | Mostly async callbacks | Both sync and async supported |
| Recursive Create/Delete | Not supported | createPersistent(path, true) / deleteRecursive |
Complete Example Code Structure
public class ZooKeeperJavaDemo {
public static void main(String[] args) throws InterruptedException {
ZkClient zkClient = new ZkClient("h121.wzk.icu:2181");
// 1. Create node
zkClient.createPersistent("/wzk-java/data", true);
zkClient.writeData("/wzk-java/data", "hello");
// 2. Register child node monitoring
zkClient.subscribeChildChanges("/wzk-java", (parent, children) -> {
System.out.println("Children changed: " + children);
});
// 3. Register data monitoring
zkClient.setZkSerializer(new SerializableSerializer());
zkClient.subscribeDataChanges("/wzk-java/data", new IZkDataListener() {
public void handleDataChange(String path, Object data) {
System.out.println("Data changed: " + data);
}
public void handleDataDeleted(String path) {
System.out.println("Node deleted: " + path);
}
});
// 4. Wait for events
Thread.sleep(60_000);
// 5. Cleanup
zkClient.deleteRecursive("/wzk-java");
System.out.println("Cleanup done.");
}
}
Summary
ZkClient significantly simplifies ZooKeeper Java programming complexity: recursive node operations, auto reconnection, persistent Watcher are all out-of-the-box. In production, Curator Framework (Apache maintained) provides more complete high-level abstractions (distributed locks, Leader election, etc.), is the advanced alternative to ZkClient. ZooKeeper basics series ends here, next phase will enter Kafka message queue learning.