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("节点删除成功");
}
}
View Code
说明:
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);
}
}
}
View Code
说明:
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();
}
}
View Code
说明:
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("成功删除");
}
}