在zookeeper的API使用中,我们已经知道了原生API的使用,但是原生api的Watcher注册反复注册,session超时重连等功能,都比较繁琐,所以有了各自开源客户端的出现。
maven依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
创建会话
before,创建会话用,后面不在贴这部分代码
private static ZkClient zkClient;
@Before
public void before() {
zkClient = ZkClientConnect.getZkClient();
}
ZkClientConnect
public class ZkClientConnect {
static final String CONNECT_STRING = "172.17.0.2:2181,172.17.0.3:2181,172.17.0.4:2181";
public static ZkClient getZkClient() {
// 第一个参数,是zookeeper集群各个节点的地址,多个地址用逗号隔开
// 第二个参数,会话超时时间,毫秒为单位。
ZkClient zkClient = new ZkClient(CONNECT_STRING, 5000);
return zkClient;
}
}
测试代码:
@Test
public void testConnect() {
System.out.println("连接成功");
}
运行结果如下:
原生API会话的创建,是异步的,我们之前用CountDownLatch来控制,ZkClient已经内部处理,转为同步了。另外代码看起来清爽了很多,ZkClient不需要我们传入Watcher,而是通过Listener来实现对Watcher的监听,这个下面会讲。
增删改查
创建节点
从截图的方法来看,创建节点,也简化了不少,提供了临时节点、永久节点、多层级创建等方法。
测试代码:
@Test
public void testCreate() {
zkClient.create("/node1", "node1_data", CreateMode.EPHEMERAL);
System.out.println("创建node1节点成功");
zkClient.createEphemeral("/node2");
System.out.println("创建临时节点node2成功");
zkClient.createEphemeralSequential("/node3", "node3_data");
System.out.println("创建临时有序节点node2成功");
zkClient.createPersistent("/node4", "node4_data");
System.out.println("创建持久节点node4成功");
zkClient.createPersistentSequential("/node5", "node5_data");
System.out.println("创建持久有序节点node5成功");
// 为true说明可以多层级创建
zkClient.createPersistent("/node6/node6_1", true);
System.out.println("创建持久有序节点node6成功");
try {
// 留时间截图临时节点
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果如下:
客户端查询结果如下:
[zk: localhost:2181(CONNECTED) 38] ls /
[node1, node2, node30000000021, node4, node50000000023, node6, zookeeper]
[zk: localhost:2181(CONNECTED) 39] ls /node6
[node6_1]
在原生API中,如果要创建多层级,就要一层一层的创建,创建的时候,还要判断是否已经创建节点,比较麻烦。用ZkClient就会方便很多。传入的数据,也是object,不需要传入byte[],序列化的工作ZkClient帮我们处理了,当然,我们也可以自己定义序列化工具。
删除节点
测试代码:
@Test
public void testDelete() {
zkClient.delete("/node4");
System.out.println("/node4删除成功");
zkClient.deleteRecursive("/node6");
System.out.println("/node6及子节点删除成功");
}
运行结果如下:
客户端查询结果如下:
[zk: localhost:2181(CONNECTED) 46] ls /
[node50000000023, zookeeper]
用deleteRecursive遍历删除子节点,还是非常方便的。
节点列表
测试代码:
@Test
public void testGetChildren(){
List<String> children = zkClient.getChildren("/");
System.out.println(children);
}
运行结果如下:
对比与原生API,少了Watcher。
获取数据
测试代码:
@Test
public void testReadData() {
String data = zkClient.readData("/node50000000023");
System.out.println(data);
}
运行结果如下:
获取到的结果,直接帮我们反序列化。
更新数据
测试代码:
@Test
public void testWriteData() {
zkClient.writeData("/node50000000023","node5_new_data");
String data = zkClient.readData("/node50000000023");
System.out.println(data);
}
运行结果如下:
节点是否存在
测试代码:
@Test
public void testExists() {
System.out.println("node是否存在:" + zkClient.exists("/node"));
}
运行结果如下:
这边返回的是boolean型,原生API是返回Stat。
Listener
getChildren Listener
测试代码:
@Test
public void testGetChildren4Listener() throws InterruptedException {
// 对/children的子节点监听
zkClient.subscribeChildChanges("/children", new IZkChildListener() {
// 第一个参数是父节点,第二个参数是子节点集合
public void handleChildChange(String parentPath, List<String> list) throws Exception {
System.out.println("handleChildChange--" + parentPath + ":" + list);
}
});
System.out.println("--------------1--------------");
zkClient.createPersistent("/children");
TimeUnit.MILLISECONDS.sleep(200);
System.out.println(zkClient.getChildren("/children"));
System.out.println("--------------2--------------");
zkClient.createEphemeral("/children/children_1");
TimeUnit.MILLISECONDS.sleep(200);
System.out.println(zkClient.getChildren("/children"));
System.out.println("--------------3--------------");
zkClient.writeData("/children/children_1","children_1");
TimeUnit.MILLISECONDS.sleep(200);
System.out.println(zkClient.getChildren("/children"));
zkClient.writeData("/children","children");
TimeUnit.MILLISECONDS.sleep(200);
System.out.println("--------------4--------------");
zkClient.delete("/children/children_1");
TimeUnit.MILLISECONDS.sleep(200);
System.out.println(zkClient.getChildren("/children"));
System.out.println("--------------5--------------");
zkClient.delete("/children");
TimeUnit.MILLISECONDS.sleep(200);
}
运行结果如下:
从1、5可以看出,当前节点的创建和删除,也会通知客户端。
从2、3、4可以看出,子节点的新增、删除,会通知客户端。
此外,不存在的节点也可以监听,而且不像Watcher,需要反复注册,ZkClient会一直监听。
DataChanges Listener
测试代码
@Test
public void testDataChanges4Listener() throws InterruptedException {
zkClient.subscribeDataChanges("/node", new IZkDataListener() {
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("handleDataChange--" + dataPath + ":" + data);
}
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("handleDataDeleted--" + dataPath);
}
});
zkClient.createEphemeral("/node");
TimeUnit.MILLISECONDS.sleep(200);
zkClient.writeData("/node", "data");
TimeUnit.MILLISECONDS.sleep(200);
zkClient.delete("/node");
TimeUnit.MILLISECONDS.sleep(200);
}
运行结果如下:
创建、修改的时候,调用的是handleDataChange方法,删除的时候调用的是handleDataDeleted方法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。