前面几篇演示了zookeeper的原生api,由于原生api在实现某些功能上比较繁琐,开发人员需要关注很多实现细节,因此业界出现了对zookeeper原生api的一些封装工具包。本篇简单介绍比较流行的两个开源客户端。
ZkClient
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
package com.coderman.zookeeper.clusterdemo.version2;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.util.List;
/**
* @description:
* @author: Fanchunshuai
* @time: 2020/2/12 14:50
* ZkClient是 github上的一个开源zookeeper客户端,ZkClient是在
* Zookeeper原生API接口之上进行了包装。
* ZkClient在内部实现了诸如session超时重连,Watcher反复注册等功能。
* ZkClient提供了可以自定义序列化的序列化器,可以传入复杂对象作为参数。
* Zkclient可以在内部帮忙递归建立父节点,也可以逐层删除节点。
*
*
*/
public class ZKClientDemo {
private static StringBuffer buffer = new StringBuffer();
private static final int SESSION_TIMEOUT = 5000;
static {
buffer.append("192.168.1.6:2184,");
buffer.append("192.168.1.6:2181,");
buffer.append("192.168.1.6:2182,");
buffer.append("192.168.1.6:2183,");
buffer.append("192.168.1.6:2185");
}
public static void main(String[] args) throws InterruptedException {
// initWithZkClient();
//getChildrenWithZKClient();
getDataWithZKClient();
}
/**
* 通过ZkClient链接zk集群
* 创建节点
*/
public static void initWithZkClient(){
ZkClient zkClient = new ZkClient(buffer.toString(),SESSION_TIMEOUT);
String path = "/zkclientpath";
//创建持久节点
//zkClient.createPersistent(path);
//删除持久节点
zkClient.delete(path);
System.out.println("创建节点成功.....");
}
/**
* 通过ZKClient获取子节点数据信息
* 监听子节点的变化
* 这里说明几点
* 1.客户端可以对一个不存在的节点进行子节点变更的监听
* 2.一旦客户端对一个节点注册了子节点列表变更监听之后,那么当该节点的子节点列表发生变更的时候,服务端都会
* 通知客户端,并将最新的子节点列表发送给客户端。
* 3.该节点本身的创建或者删除也会通知到客户端
* 4.zookeeper原生提供的Watcher只有一次通知,zkClient的
*Listener不是一次性的,客户端只需要注册一次就会一直生效。
*
*/
public static void getChildrenWithZKClient() throws InterruptedException {
ZkClient zkClient = new ZkClient(buffer.toString(),SESSION_TIMEOUT);
String path = "/zk-book-client";
zkClient.subscribeChildChanges(path, new IZkChildListener() {
@Override
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("parent path is changed = "+s+",current child path = "+list);
}
});
zkClient.createPersistent(path);
Thread.sleep(1000L);
System.out.println(zkClient.getChildren(path));
zkClient.createEphemeral(path+"/c1");
Thread.sleep(1000L);
zkClient.delete(path+"/c1");
Thread.sleep(1000L);
zkClient.delete(path);
}
/**
* 通过zkclient获取数据,并监听数据节点变化
* @throws InterruptedException
*/
public static void getDataWithZKClient() throws InterruptedException {
ZkClient zkClient = new ZkClient(buffer.toString(),SESSION_TIMEOUT);
String path = "/zk-book-client-3";
zkClient.createEphemeral(path,"sadfasf");
zkClient.subscribeDataChanges(path, new IZkDataListener() {
/**
* 数据内容变更通知
* @param s
* @param o
* @throws Exception
*/
@Override
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("node "+s+" changed,new Data = "+o);
}
/**
* 数据删除监听
* @param s
* @throws Exception
*/
@Override
public void handleDataDeleted(String s) throws Exception {
System.out.println("node "+s+" deleted");
}
});
System.out.println(zkClient.readData(path));
zkClient.writeData(path,"24342342");
Thread.sleep(1000L);
zkClient.delete(path);
Thread.sleep(1000L);
}
}
Curator
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
package com.coderman.zookeeper.clusterdemo.version2;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
/**
* @description:
* @author: Fanchunshuai
* @time: 2020/2/12 15:33
* zookeeper的另一个封装的api,Curator,其优点如下
* 1.封装开发人员不需要特别关注的底层细节
* 2.封装zookeeper原生api的接口,提供了一套易用性和可读性更强的
* Fluent风格的客户端API框架
* 3.Curator还提供了Zookeeper各种应用场景(Recipe,如共享锁服务,
* Master选举机制和分布式计数器等)的抽象封装
*
* 本代码只演示了一少部分api,其他可以参考《从Paxos到zookeeper分布式一致性原理与实践》
*/
public class ZKCuratorDemo {
private static final int SESSION_TIMEOUT = 5000;
private static final int CONNECT_TIMEOUT = 3000;
private static StringBuffer buffer = new StringBuffer();
static {
buffer.append("192.168.1.6:2184,");
buffer.append("192.168.1.6:2181,");
buffer.append("192.168.1.6:2182,");
buffer.append("192.168.1.6:2183,");
buffer.append("192.168.1.6:2185");
}
public static void main(String[] args) throws InterruptedException {
initWithCurator();
}
/**
* 使用curator进行初始化连接zookeeper集群
*/
public static void initWithCurator() throws InterruptedException {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient(buffer.toString(),SESSION_TIMEOUT,CONNECT_TIMEOUT,retryPolicy);
client.start();
Thread.sleep(Integer.MAX_VALUE);
}
/**
* 为了实现不同的zookeeper业务之间的隔离,往往会为每个业务分配一个
* 独立的命名空间,即指定一个zookeeper根路径。
*/
public static void buildNameSpace(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFrameworkFactory.builder().connectString(buffer.toString())
.sessionTimeoutMs(SESSION_TIMEOUT)
.retryPolicy(retryPolicy)
.namespace("busniessline1").build();
}
/**
* 使用Curator创建节点等操作
*/
public static void createNode() throws Exception {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.builder().connectString(buffer.toString())
.sessionTimeoutMs(SESSION_TIMEOUT)
.retryPolicy(retryPolicy)
.namespace("busniessline1").build();
String path = "/zk-book-ooo/c1";
client.start();
//创建节点
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path,"init".getBytes());
//读取数据
byte[] bytes = client.getData().forPath(path);
//更新数据
client.setData().forPath(path,"sdafsd".getBytes());
//删除节点
client.delete().forPath(path);
}
}