一、常用zk java 客户端
- zk原生api
- zkclient
- apache curator
二、zk原生api的不足之处
- 超时重连,不支持自动,需要手动操作
- Watch注册一次后会失效
- 不支持递归创建节点
三、Apache curator
- apache的开源项目
- 解决watcher的注册一次就失效
- api更加简单易用
- 提供更多解决方案并且实现简单:比如分布式锁
- 提供常用的Zookeeper工具类
- 编程风格更爽
增删改查监听实例:
public class CuratorOperator { public CuratorFramework client = null; public static final String zkServerPath = "192.168.87.130:2181,192.168.87.131:2181,192.168.87.132:2181"; /** * 实例化zk客户端 */ public CuratorOperator() { /** * 同步创建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() .connectString(zkServerPath) .sessionTimeoutMs(10000).retryPolicy(retryPolicy) .namespace("workspace").build(); client.start(); } /** * * @Description: 关闭zk客户端连接 */ public void closeZKClient() { if (client != null) { this.client.close(); } } public static void main(String[] args) throws Exception { // 实例化 CuratorOperator cto = new CuratorOperator(); boolean isZkCuratorStarted = cto.client.isStarted(); System.out.println("当前客户的状态:" + (isZkCuratorStarted ? "连接中" : "已关闭")); // 创建节点 String nodePath = "/super/imooc"; byte[] data = "superme".getBytes(); cto.client.create().creatingParentsIfNeeded() .withMode(CreateMode.PERSISTENT) .withACL(Ids.OPEN_ACL_UNSAFE) .forPath(nodePath, data); // 更新节点数据 byte[] newData = "batman".getBytes(); cto.client.setData().withVersion(0).forPath(nodePath, newData); // 删除节点(自动递归删除) cto.client.delete() .guaranteed() // 如果删除失败,那么在后端还是继续会删除,直到成功 .deletingChildrenIfNeeded() // 如果有子节点,就删除 .withVersion(1) .forPath(nodePath); // 读取节点数据 Stat stat = new Stat(); byte[] data = cto.client.getData().storingStatIn(stat).forPath(nodePath); System.out.println("节点" + nodePath + "的数据为: " + new String(data)); System.out.println("该节点的版本号为: " + stat.getVersion()); // 查询子节点 List<String> childNodes = cto.client.getChildren() .forPath(nodePath); System.out.println("开始打印子节点:"); for (String s : childNodes) { System.out.println(s); } // 判断节点是否存在,如果不存在则为空 Stat statExist = cto.client.checkExists().forPath(nodePath + "/abc"); System.out.println(statExist); // watcher 事件 当使用usingWatcher的时候,监听只会触发一次,监听完毕后就销毁 cto.client.getData().usingWatcher(new MyCuratorWatcher()).forPath(nodePath); cto.client.getData().usingWatcher(new MyWatcher()).forPath(nodePath); // 为节点添加watcher(仅对当前节点变更会触发事件) // NodeCache: 监听数据节点的变更,会触发事件 final NodeCache nodeCache = new NodeCache(cto.client, nodePath); // buildInitial : 初始化的时候获取node的值并且缓存 nodeCache.start(true); 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); } }); // 为子节点添加watcher(对子节点变更会触发事件) // PathChildrenCache: 监听数据节点的增删改,会触发事件 String childNodePathCache = nodePath; // cacheData: 设置缓存节点的数据状态 final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, childNodePathCache, true); /** * StartMode: 初始化方式 * POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件 * NORMAL:异步初始化 * BUILD_INITIAL_CACHE:同步初始化 */ childrenCache.start(StartMode.POST_INITIALIZED_EVENT); List<ChildData> childDataList = childrenCache.getCurrentData(); System.out.println("当前数据节点的子节点数据列表:"); for (ChildData cd : childDataList) { String childData = new String(cd.getData()); System.out.println(childData); } 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(); if (path.equals(ADD_PATH)) { System.out.println("添加子节点:" + event.getData().getPath()); System.out.println("子节点数据:" + new String(event.getData().getData())); } else if (path.equals("/super/imooc/e")) { System.out.println("添加不正确..."); } else { System.out.println("什么情况" + event.getData()); } }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())); } } }); Thread.sleep(1000000); cto.closeZKClient(); boolean isZkCuratorStarted2 = cto.client.isStarted(); System.out.println("当前客户的状态:" + (isZkCuratorStarted2 ? "连接中" : "已关闭")); } public final static String ADD_PATH = "/super/imooc/d"; }