1.前言
之前已经说了zookeeper的原生的API,我们也知道了原生api不足之处:
- 超时重连,不支持自动
- Watcher注册一次后则会失效
- 不支持递归读写操作
因此本篇博客所讲的Curator框架就是问了解决上述问题的
Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册,提供更多解决方案:如分布式锁以及Watcher和NodeExistsException异常等等。
接下来看一下具体的实现代码:
2.具体实现
连接客户端
public CuratorFramework client = null;
public static final String zkServerPath = "127.0.0.1:2181";
/**
* 同步创建zk示例,原生api是异步的
*
* curator链接zookeeper的策略:ExponentialBackoffRetry
* baseSleepTimeMs:初始sleep的时间
* maxRetries:最大重试次数
* maxSleepMs:最大重试时间
*/
// RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
/**
* curator链接zookeeper的策略:RetryNTimes
* n:重试的次数
* sleepMsBetweenRetries:每次重试间隔的时间
*/
//RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
/**
* curator链接zookeeper的策略:RetryOneTime
* sleepMsBetweenRetry:每次重试间隔的时间
*/
RetryPolicy retryPolicy2 = new RetryOneTime(3000);
/**
* 永远重试,不推荐使用
*/
// RetryPolicy retryPolicy3 = new RetryForever(retryIntervalMs)
/**
* curator链接zookeeper的策略:RetryUntilElapsed
* maxElapsedTimeMs:最大重试时间
* sleepMsBetweenRetries:每次重试间隔
* 重试时间超过maxElapsedTimeMs后,就不再重试
*/
// RetryPolicy retryPolicy4 = new RetryUntilElapsed(2000, 3000);
client = CuratorFrameworkFactory.builder()
// .authorization("digest","id1:123456".getBytes())
.connectString(zkServerPath)
.sessionTimeoutMs(10000).retryPolicy(retryPolicy2)
.namespace("workspace").build();
client.start();
boolean isZkCuratorStarted = cto.client.isZk34CompatibilityMode();
System.out.println("当前客户的状态:" + (isZkCuratorStarted ? "连接中" : "已关闭"));
输出:当前客户的状态:连接中
关闭客户端连接
public void closeZKClient() {
if (client != null) {
this.client.close();
}
}
创建节点
可以递归创建
public static void createNode(CuratorOperator cto) throws Exception {
String path = "/super/n2";
byte [] data = "ldd-data".getBytes();
cto.client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath(path,data);
}
更新节点
public static void updateNode(CuratorOperator cto) throws Exception{
String path = "/super/n2";
byte[] newData = "new-data".getBytes();
cto.client.setData().withVersion(0).forPath(path, newData);
}
删除节点
public static void deleteNode(CuratorOperator cto)throws Exception{
String path = "/super/n2";
cto.client.delete()
.guaranteed() // 如果删除失败,那么在后端还是继续会删除,直到成功
.deletingChildrenIfNeeded() // 如果有子节点,就删除
.withVersion(0)
.forPath(path);
}
读取节点数据
public static void readNode(CuratorOperator cto)throws Exception{
Stat stat = new Stat();
String path = "/super/n2";
byte[] data = cto.client
.getData()
.storingStatIn(stat)
.forPath(path);
System.out.println("data: "+new String(data));
System.out.println("最新的版本号: "+stat.getVersion());
}
查询子节点
public static void selectChildNode(CuratorOperator cto)throws Exception{
String path = "/super";
List<String> list = cto.client
.getChildren()
.forPath(path);
for(String list1:list){
System.out.println(list1);
}
}
输出n2
[zk: localhost:2181(CONNECTED) 32] ls /workspace/super
[n2]
[zk: localhost:2181(CONNECTED) 33]
判断节点是否存在
如果没有此节点则返回null
public static void isExistNode(CuratorOperator cto)throws Exception{
String path = "/super";
Stat stat = cto.client
.checkExists().forPath(path);
System.out.println(stat);
}
读取数据添加Watcher
public static void readNodeByWatcher(CuratorOperator cto)throws Exception{
String path = "/super/n2";
byte[] data = cto.client
.getData()
// watcher 事件 当使用usingWatcher的时候,监听只会触发一次,监听完毕后就销毁
.usingWatcher(new MyCuratorWatcher())
//.usingWatcher(new MyWatcher()).forPath(nodePath);
.forPath(path);
System.out.println("data: "+new String(data));
}
public class MyCuratorWatcher implements CuratorWatcher {
@Override
public void process(WatchedEvent event) {
System.out.println("触发路径类型:"+event.getType());
}
}
readNodeByWatcher(cto);
new Thread().sleep(1000000);
cto.closeZKClient();
[zk: localhost:2181(CONNECTED) 50] set /workspace/super/n2 n
将会输出:
触发路径类型:NodeDataChanged
节点缓存
final NodeCache nodeCache = new NodeCache(cto.client,path);
nodeCache.start(true);//如果不写参数默认是false
if (nodeCache.getCurrentData() != null) {
System.out.println("节点初始化数据为:" + new String(nodeCache.getCurrentData().getData()));
} else {
System.out.println("节点初始化数据为空...");
}
nodeCache.getListenable().addListener(new NodeCacheListener() {
public void nodeChanged() throws Exception {
if (nodeCache.getCurrentData() == null) {
System.out.println("空");
return;
}
String data = new String(nodeCache.getCurrentData().getData());
System.out.println("节点路径:" + nodeCache.getCurrentData().getPath() + "数据:" + data);
}
});
当前客户的状态:连接中
节点初始化数据为:n
节点路径:/super/n2数据:n2
节点路径:/super/n2数据:n3
节点路径:/super/n2数据:n4
节点路径:/super/n2数据:n5
节点路径:/super/n2数据:n6
[zk: localhost:2181(CONNECTED) 49] set /workspace/super/n2 n
[zk: localhost:2181(CONNECTED) 50] set /workspace/super/n2 n
[zk: localhost:2181(CONNECTED) 51] set /workspace/super/n2 n2
[zk: localhost:2181(CONNECTED) 52] set /workspace/super/n2 n3
[zk: localhost:2181(CONNECTED) 53] set /workspace/super/n2 n4
[zk: localhost:2181(CONNECTED) 54] set /workspace/super/n2 n5
[zk: localhost:2181(CONNECTED) 55] set /workspace/super/n2 n6
子节点缓存
BUILD_INITIAL_CACHE 会打印数据
String childNodePathCache = "/super";
// cacheData: 设置缓存节点的数据状态
final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, childNodePathCache, true);
/**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
* NORMAL:异步初始化
* BUILD_INITIAL_CACHE:同步初始化 会打印数据
*/
childrenCache.start(StartMode.BUILD_INITIAL_CACHE);
List<ChildData> childDataList = childrenCache.getCurrentData();
System.out.println(childDataList.size());
System.out.println("当前数据节点的子节点数据列表:");
for (ChildData cd : childDataList) {
String childData = new String(cd.getData());
System.out.println(childData);
}
POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
childrenCache.start(StartMode.POST_INITIALIZED_EVENT);
List<ChildData> childDataList = childrenCache.getCurrentData();
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
if (event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)) {
System.out.println("子节点初始化ok...");
}
readNodeByWatcher(cto);
new Thread().sleep(100000);
cto.closeZKClient();
子节点初始化ok...
Watcher类型触发:
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
if (event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)) {
System.out.println("子节点初始化ok...");
}
else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
String path = event.getData().getPath();
System.out.println("添加新节点: "+path);
// System.out.println("data: "+event.getData());
if (path.equals("/super/nn1")) {
System.out.println("添加子节点:" + event.getData().getPath());
System.out.println("子节点数据:" + new String(event.getData().getData()));
} else if (path.equals("/super/ldd/e")) {
System.out.println("添加不正确...");
}
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
System.out.println("删除子节点:" + event.getData().getPath());
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
System.out.println("修改子节点路径:" + event.getData().getPath());
System.out.println("修改子节点数据:" + new String(event.getData().getData()));
}
}
});
new Thread().sleep(100000);
cto.closeZKClient();