
这个框架 , 相对于Zk提供官方客户端, 功能更多一些. 也很实用. 其实就是封装了一下.
这一章节 , 基本上就是介绍这个API的简单的使用.下一章节, 主要是介绍 它的分布式锁 , 选举机制的使用.
1. 简单使用一下API.
启动很简单. 直接这么编程就可以了. 可以加入几个启动的监听器.
创建客户端
private static CuratorFramework getCuratorFramework() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.58.131:2181", retryPolicy);
client.getConnectionStateListenable().addListener((c, newState) -> System.out.println("connect : " + c));
//启动
client.start();
return client;
}
实用builder 创建客户端.
// 指数. 增长
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
// 这个意思其实是初始化的时候创建一个父节点.记住不需要加 / ,该客户端所有的操作都基于该空间上 CRUD.
.namespace("node")
.connectionTimeoutMs(1000)
.connectString("192.168.58.131:2181")
.retryPolicy(retryPolicy)
.build();
// 启动
client.start();
创建一个节点.
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 路径
String path = "/create-data/eph_";
// 创建一个节点. 临时自增节点. 完全开放的策略. 数据为init
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(path, "init".getBytes());
// 获取子节点.
List<String> list = client.getChildren().forPath("/create-data");
list.forEach(System.out::println); //eph_0000000000
}
zk 3.5 后面加入了一个容器节点. 当容器类型的节点下的子节点全部被删除时,则该节点也会在一段时间内被删除。
判断一个节点是否存在.
监测是节点是否存在 :
private static void checkExist(String path) throws Exception {
CuratorFramework client = getCuratorFramework();
// 调用 checkExists() 方法.
Stat stat = client.checkExists().forPath(path);
if (stat == null) {
System.out.println("不存在");
} else {
System.out.println("存在 : " + stat);
}
client.close();
}
获取节点数据 :
private static void getData(String path) throws Exception {
CuratorFramework client = getCuratorFramework();
byte[] bytes = client.getData().forPath(path);
System.out.println("data : " + new String(bytes));
}
获取子节点
List<String> list = client.getChildren().forPath("/test2");
添加watcher
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 路径
String path = "/create-data-exit";
// 添加监听器.
client.getData().usingWatcher(new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getType());
}
}).forPath(path);
client.setData().forPath(path, "change".getBytes());
}
2. 重复监听
一下三种对于并发写入都是不支持 , 全部都可以监听到的.
我们自己写一个 重复监听. 其实就是一个递归式
public class App4 {
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 先删除
String path = "/test-watcher";
client.delete().deletingChildrenIfNeeded().forPath(path);
// 创建当前节点
client.create().withMode(CreateMode.PERSISTENT).forPath(path, "init".getBytes());
// 重复监听
watch(client, path);
client.setData().forPath(path, "change-1".getBytes());
client.setData().forPath(path, "change-2".getBytes());
System.in.read();
}
private static void watch(CuratorFramework client, String path) throws Exception {
client.getData().usingWatcher(new CuratorWatcher() {
@Override
public void process(WatchedEvent event) throws Exception {
System.out.println(event.getPath() + " : " + event.getType());
// 递归重复监听.
watch(client, path);
}
}).forPath(path);
}
}
NodeCache 单节点监听.
可以帮助我们做一些事情. 就是重复监听.
A utility that attempts to keep the data from a node locally cached. This class will watch the node, respond to update/create/delete events, pull down the data, etc. You can register a listener that will get notified when changes occur.
public class App {
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 先删除
String path = "/test-watcher";
client.delete().forPath(path);
// 创建当前节点
client.create().withMode(CreateMode.PERSISTENT).forPath(path, "hello world - init".getBytes());
// 创建一个 NodeCache
NodeCache cache = new NodeCache(client, path);
// true : 会保存初始化信息. 原状态信息 , false不会
cache.start(true);
// 所以这里可以直接拿到初始化信息
System.out.println("初始化信息 :" + new String(cache.getCurrentData().getData()));
// 添加listener.
cache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("当前节点发生改变:" + new String(cache.getCurrentData().getData()));
}
});
ExecutorService pool = Executors.newFixedThreadPool(5);
IntStream.range(0, 10).forEach(value -> pool.execute(() -> {
try {
// 改变值. 并发改变
client.setData().forPath(path, String.format("change-%d", value).getBytes());
} catch (Exception e) {
//
}
}));
}
}
结果是什么?
当前节点发生改变:hello world - init
当前节点发生改变:change-9
是不是很吃惊. 因为这个回调本身没有做同步机制. 所以对于高的写入修改可能做不到及时的监听. 也正常.
PathChildrenCache
只监听改目录下的第一级子节点.
public class App3 {
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 先删除
String path = "/test-watcher";
client.delete().deletingChildrenIfNeeded().forPath(path);
// 创建当前节点
client.create().withMode(CreateMode.PERSISTENT).forPath(path, "hello world - 1".getBytes());
// 创建一个 PathChildrenCache
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
/**
* NORMAL:正常模式
* BUILD_INTIAL_CACHE:启动什么事件都不输出
* POST_INITAL_CACHE:启动会输出初始化事件
*/
childrenCache.start(PathChildrenCache.StartMode.NORMAL);
// 添加listener.
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println(String.format("path: %s, type: %s,value: %s.", event.getData().getPath(), event.getType(), new String(event.getData().getData())));
}
});
ExecutorService pool = Executors.newFixedThreadPool(5);
IntStream.range(0, 10).forEach(value -> pool.execute(() -> {
try {
String path_c = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path + path + "_child_" + value, "init".getBytes());
client.setData().forPath(path_c, String.format("change-%d", value).getBytes());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}));
}
}
输出 : 我发现它不能监听子节点的创建.
path: /test-watcher/test-watcher_child_4, type: CHILD_ADDED,value: init.
path: /test-watcher/test-watcher_child_3, type: CHILD_ADDED,value: init.
path: /test-watcher/test-watcher_child_2, type: CHILD_ADDED,value: init.
path: /test-watcher/test-watcher_child_1, type: CHILD_ADDED,value: init.
path: /test-watcher/test-watcher_child_0, type: CHILD_ADDED,value: init.
path: /test-watcher/test-watcher_child_1, type: CHILD_UPDATED,value: change-1.
path: /test-watcher/test-watcher_child_2, type: CHILD_UPDATED,value: change-2.
path: /test-watcher/test-watcher_child_0, type: CHILD_UPDATED,value: change-0.
path: /test-watcher/test-watcher_child_4, type: CHILD_UPDATED,value: change-4.
path: /test-watcher/test-watcher_child_3, type: CHILD_UPDATED,value: change-3.
path: /test-watcher/test-watcher_child_8, type: CHILD_ADDED,value: change-8.
path: /test-watcher/test-watcher_child_7, type: CHILD_ADDED,value: change-7.
path: /test-watcher/test-watcher_child_6, type: CHILD_ADDED,value: change-6.
path: /test-watcher/test-watcher_child_5, type: CHILD_ADDED,value: change-5.
path: /test-watcher/test-watcher_child_9, type: CHILD_ADDED,value: change-9.
反正我感觉监听不准确. 并且还有点乱. 无非是因为 zk 是一个 写少读多的场景.
TreeCache
这个很简单其实就是 , 子级目录也监听.
public static void main(String[] args) throws Exception {
// 启动zk客户端
CuratorFramework client = Zk.getCuratorFramework();
// 先删除
String path = "/test-watcher";
client.delete().deletingChildrenIfNeeded().forPath(path);
// 创建当前节点
client.create().withMode(CreateMode.PERSISTENT).forPath(path, "init".getBytes());
TreeCache cache = new TreeCache(client, path);
cache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
// todo
}
});
}
基本写法一样.