Java操作zookeeper
Java操作zookeeper总共有三种方式:
1.原生的Java API
2.zkclient
3.curator
第一种实现代码:
pom.xml
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.8</version> </dependency>
示例的java代码如下:
package zook; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; public class App { public static void main(String[] args) throws IOException, InterruptedException, KeeperException { String connStr = "192.168.126.128:2181"; CountDownLatch countDown = new CountDownLatch(1); Watcher watcher=new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == KeeperState.SyncConnected) { System.err.println("eventType:"+event.getType()); if(event.getType()==Event.EventType.None){ countDown.countDown(); }else if(event.getType()==Event.EventType.NodeCreated){ System.out.println("listen:节点创建"); }else if(event.getType()==Event.EventType.NodeChildrenChanged){ System.out.println("listen:子节点修改"); } } } }; ZooKeeper zookeeper = new ZooKeeper(connStr, 5000,watcher ); countDown.await(); //注册监听,每次都要重新注册,否则监听不到 zookeeper.exists("/top/jinyong", watcher); // 创建节点 String result = zookeeper.create("/top/jinyong", "一生一世".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(result); Thread.sleep(10); // 获取节点 byte[] bs = zookeeper.getData("/top/jinyong", true, null); result = new String(bs); System.out.println("创建节点后的数据是:" + result); // 修改节点 zookeeper.setData("/top/jinyong", "I love you".getBytes(), -1); Thread.sleep(10); bs = zookeeper.getData("/top/jinyong", true, null); result = new String(bs); System.out.println("修改节点后的数据是:" + result); // 删除节点 zookeeper.delete("/top/jinyong", -1); System.out.println("节点删除成功"); } }
说明:
1.会话连接是异步的,需要自己去处理。此处用的CountDownLatch
2.Watch需要重复注册,不然就不能生效,比如开始的zookeeper.exists("/top/jinyong", watcher);就是为了注册监听
3.开发的复杂性还是比较高的
4.不支持多节点删除和创建。需要自己去递归。后面有一个关于递归的示例。
第二种实现:
pom.xml
<dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency>
示例的Java代码如下:
package zook; import java.util.List; import org.I0Itec.zkclient.IZkChildListener; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.IZkStateListener; import org.I0Itec.zkclient.ZkClient; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher.Event.KeeperState; public class Client { public static void main(String[] args) throws InterruptedException { String connStr = "192.168.126.128:2181"; ZkClient zk = new ZkClient(connStr); // 注册【数据】事件 zk.subscribeDataChanges("/top/zhuzhu", new IZkDataListener() { @Override public void handleDataDeleted(String arg0) throws Exception { System.err.println("数据删除:" + arg0); } @Override public void handleDataChange(String arg0, Object arg1) throws Exception { System.err.println("数据修改:" + arg0 + "------" + arg1); } }); zk.subscribeChildChanges("/top", new IZkChildListener() { @Override public void handleChildChange(String arg0, List<String> arg1) throws Exception { System.err.println("子节点发生变化:" + arg0); arg1.forEach(f -> { System.out.println("content:" + f); }); } }); List<String> list = zk.getChildren("/"); list.forEach(e -> { System.out.println(e); }); String res = zk.create("/top/zhuzhu", "I love you", CreateMode.PERSISTENT); System.out.println("创建节点/top/zhuzhu成功:" + res); zk.writeData("/top/zhuzhu", "forerver"); System.out.println("修改节点/top/zhuzhu数据成功"); res = zk.readData("/top/zhuzhu"); System.out.println("节点数据:" + res); Thread.sleep(1000); zk.delete("/top/zhuzhu"); System.out.println("删除节点/top/zhuzhu成功"); Thread.sleep(1000); System.out.println("------------------------------------------------"); for (int i = 0; i < 10; i++) { zk.create("/top/zhuzhu", "I love you", CreateMode.PERSISTENT); Thread.sleep(1000); zk.delete("/top/zhuzhu"); Thread.sleep(1000); } } }
说明:
1.subscribe开头的为注册监听的一些方法
2.addAuthInfo和setAcl为权限相关控制
3.普通使用这种方式还是值得推荐的
第三种实现:
pom.xml
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.11.0</version> </dependency>
示例的Java代码如下:
package zook; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; public class Curator { public static void main(String[] args) throws Exception { String connStr = "192.168.23.24:2181"; CuratorFramework cur=CuratorFrameworkFactory.builder() .connectString(connStr) .connectionTimeoutMs(5000) .retryPolicy(new ExponentialBackoffRetry(1000,3)) .build(); cur.start();//连接 //创建监听 PathChildrenCache cache=new PathChildrenCache(cur,"/top",true); cache.start(); cache.rebuild(); cache.getListenable().addListener(new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework framwork, PathChildrenCacheEvent event) throws Exception { System.err.println("节点发生变化:"+event.getType()); } }); Stat stat=cur.checkExists().forPath("/top/zhuzhu"); if(stat!=null){ System.out.println("【/top/zhuzhu】节点存在,直接删除"); cur.delete().forPath("/top/zhuzhu"); } cur.delete().forPath("/top/zhuzhu"); System.in.read(); System.out.println("准备创建【/top/zhuzhu】"); cur.create().withMode(CreateMode.PERSISTENT) .forPath("/top/zhuzhu", "love forever".getBytes()); System.out.println("节点【/top/zhuzhu】创建成功"); Thread.sleep(1000); byte[] bs=cur.getData().forPath("/top/zhuzhu"); System.out.println("数据:"+new String(bs)); Thread.sleep(1000); cur.delete().forPath("/top/zhuzhu"); Thread.sleep(1000); } /** * 三种watcher来做节点的监听 * pathcache 监视一个路径下子节点的创建、删除、节点数据更新 * NodeCache 监视一个节点的创建、更新、删除 * TreeCache pathcaceh+nodecache 的合体(监视路径下的创建、更新、删除事件), * 缓存路径下的所有子节点的数据 */ public static void main1(String[] args) throws Exception { String connStr = "192.168.23.24:2181"; CuratorFramework curatorFramework=CuratorFrameworkFactory.builder() .connectString(connStr) .connectionTimeoutMs(5000) .retryPolicy(new ExponentialBackoffRetry(1000,3)) .build(); curatorFramework.start(); /** * 节点变化NodeCache */ /* NodeCache cache=new NodeCache(curatorFramework,"/curator",false); cache.start(true); cache.getListenable().addListener(()-> System.out.println("节点数据发生变化,变化后的结果" + ":"+new String(cache.getCurrentData().getData()))); curatorFramework.setData().forPath("/curator","菲菲".getBytes());*/ /** * PatchChildrenCache */ PathChildrenCache cache=new PathChildrenCache(curatorFramework,"/event",true); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); cache.rebuild(); // Normal / BUILD_INITIAL_CACHE /POST_INITIALIZED_EVENT cache.getListenable().addListener((curatorFramework1,pathChildrenCacheEvent)->{ switch (pathChildrenCacheEvent.getType()){ case CHILD_ADDED: System.out.println("增加子节点"); break; case CHILD_REMOVED: System.out.println("删除子节点"); break; case CHILD_UPDATED: System.out.println("更新子节点"); break; default:break; } }); // curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/event","event".getBytes()); // TimeUnit.SECONDS.sleep(1); // System.out.println("1"); // curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/event/event1","1".getBytes()); // TimeUnit.SECONDS.sleep(1); // System.out.println("2"); // // curatorFramework.setData().forPath("/event/event1","222".getBytes()); // TimeUnit.SECONDS.sleep(1); // System.out.println("3"); curatorFramework.delete().forPath("/event/event1"); System.out.println("4"); System.in.read(); } }
说明:
1.支持事务
2.支持Flush写法
3.开始测试多次程序启动就执行删除节点,而监听的结果确实新增,后来加了cache.rebuild();代码就没问题了。跟源码,在cache.start()里面有一个构造函数也是调用了rebuild方法的。
4.功能还是比较强大的。高级功能都会用到这种方式
最后贴一个原生API的递归操作方式:
package zook; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; public class ZookManager { ZooKeeper zookeeper = null; public ZookManager(String connStr) throws IOException, InterruptedException { CountDownLatch latch = new CountDownLatch(1); zookeeper = new ZooKeeper(connStr, 5000, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getType() == EventType.None) { if (event.getState() == KeeperState.SyncConnected) { latch.countDown(); } else { System.out.println("连接失败."); latch.countDown(); } } } }); latch.await(); } /** 创建节点,不存在父节点将新增,如果节点已经存在将抛出异常 **/ public String create(String path, String val) throws KeeperException, InterruptedException { if (!checkPath(path)) { return ""; } String p = getParentPath(path); cycleCreate(p); String url = zookeeper.create(path, val.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); return url; } /** 设置节点的数据,如果节点不存在将新增该节点 **/ public Stat setData(String path, String val) throws KeeperException, InterruptedException { if (!checkPath(path)) { return null; } cycleCreate(path); return zookeeper.setData(path, val.getBytes(), -1); } /** 删除节点,如果存在子节点将递归删除 * @throws InterruptedException * @throws KeeperException **/ public void delete(String path) throws KeeperException, InterruptedException { if (!checkPath(path)) { return; } List<String> chidren = zookeeper.getChildren(path, false); for (String p : chidren) { delete(path + "/" + p); } zookeeper.delete(path, -1); } private void cycleCreate(String path) throws KeeperException, InterruptedException { Stat stat = zookeeper.exists(path, null); if (stat == null) { String p = getParentPath(path); cycleCreate(p);// 递归 // 创建 zookeeper.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } /** * 检查目录是否正确 * @param path * @return */ private boolean checkPath(String path) { if (!path.startsWith("/")) { System.err.println("路径必须以/开头:" + path); return false; } if (path.endsWith("/")) { System.err.println("路径不能以/结尾:" + path); return false; } if (path.contains("//")) { System.err.println("路径格式不对,存在连续的/:" + path); return false; } if (path.equals("/")) { System.err.println("路径格式不对,只有一个/:" + path); return false; } return true; } /** * 获得父级目录 * @param path /root/abc * @return */ private String getParentPath(String path) { int index = path.lastIndexOf("/"); return path.substring(0, index); } public static void main(String[] args) throws IOException, InterruptedException, KeeperException { ZookManager zoo = new ZookManager("192.168.23.24:2181"); zoo.setData("/top/enjoy/abc", "abc"); zoo.setData("/top/enjoy/bbb", "bbb"); zoo.setData("/top/enjoy/ccc", "ccc"); System.out.println("成功新增"); zoo.delete("/top/enjoy"); System.out.println("成功删除"); } }