其实对Zookeeper原生API封装的框架有两个,一个是zkClient,一个是Curator。但是我个人觉得呢,封装的框架是给我们提供方便以及更强大的功能,这当然是非常棒的了,但是,如果我们能先学习好原生API,到学框架时,就能理解得更深了,也知道它是怎么做到比原生更加方便更加强大的功能。
至于原生API的详解我就不贴上来了,因为网上一大堆比我厉害的人,哈哈。下面我会将我知道的需要注意的点写一下,然后把我自己调用原生API的代码贴出来给大家瞄瞄。
1、创建的临时节点:本次会话有效,当本次会话连接断开了就被删除掉。
可以利用临时节点做个分布式锁。先get判断再create,而不是直接create是否抛异常来判断能否创建。Zookeeper的数据是存放在内存中的,所以get的性能非常的好。
2、不允许递归创建节点:
例如要创建/testRoot/test1,如果没有/testRoot节点,就这样创建,是会报错的
而Curator框架是可以递归创建节点的。
3、一般操作都提供两套API,一个是同步,一个是异步,要使用异步操作要注册一个异步回调函数,要实现AsynCallBack.xxx 接口。
4、delete操作,一个参数是节点的path,第二个是数据版本号dataVersion,写-1就是跳过版本检查。如果不是-1,会删除对应的版本号节点。
5、获取当前节点的子节点,返回的字符串列表中,字符串是子节点的相对路径,而不是绝对路径,如果想得到子节点的数据,记得是根据绝对路径获取。而且,如果想获取当前节点的子节点下的子节点,是不能直接获取到的。
6、创建不支持递归,删除也不支持递归。如果被删除的节点下有子节点,是会报错的。KeeperErrorCode = Directory not empty for xxx
Demo代码:代码中也会有比较详细的注解
package com.demo.zookeeper.base;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback.StringCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZookeeperBase {
/**zookeeper地址*/
private static final String CONNECT_ADDR="192.168.1.111:2181,192.168.1.112:2181,192.168.1.113:2181";
/**session超时时间*/
private static final int SESSION_OUTTIME = 2000;//单位为ms
/**信号量,阻塞主线程执行,用于等待Zookeeper连接成功,发送成功信号*/
private static final CountDownLatch semaphore = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, new Watcher(){
@Override
public void process(WatchedEvent event) {
//获取事件的状态
KeeperState keeperState =event.getState();
//节点事件
EventType eventType = event.getType();
if(KeeperState.SyncConnected == keeperState){//连接成功
if(EventType.None == eventType){ //第一次连接EventType的状态都为none
//如果建立连接成功,则发送成功的信号量,让后续程序继续执行
semaphore.countDown();
System.out.println("zk建立连接");
}
}
}
});
//进行阻塞
semaphore.await();
/**
* 创建父节点
* data:字节数组,表示不支持序列化
* Ids.OPEN_ACL_UNSAFE 表示不设置访问控制
* CreateMode.EPHEMERAL 短暂,PERSISTENT 持久 ,如果后面带上SEQUENTIAL表示要顺序节点
*/
zk.create("/testRoot", "testRoot".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
/**
* 创建子节点,试试异步方式
*/
//zk.create("/testRoot/test1", "test1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/testRoot/test1", "test1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new StringCallback(){
/**
* rc:服务端响应码 0表示调用成功,-4表示端口连接,-110表示节点存在,-12表示会话已经过期
* path:接口调用时传入API的数据节点的路径参数
* ctx:为调用接口时传入API的ctx值
* name:实际在服务端创建节点的名称
*/
@Override
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println("rc:"+rc);
System.out.println("path:"+path);
System.out.println("ctx:"+ctx);
System.out.println("name:"+name);
}
}, "666");//666对应里面的ctx
//获取节点信息
byte[] data = zk.getData("/testRoot", false, null);
System.out.println("父节点信息:"+new String(data));
/**
* 获取子节点信息,注意:path填的是父节点的path,返回的应该是子节点的path(相对的)列表
*/
List<String> pathList = zk.getChildren("/testRoot", false);
System.out.println(pathList);
/**
* 修改节点的值
* 第一个参数:节点的path
* 第二个参数:节点的新值
* 第三个参数:版本号 -1表示不理睬版本号
*/
zk.setData("/testRoot", "newRoot".getBytes(), -1);
System.out.println(zk.getData("/testRoot", false, null));
/**
* 判断节点是否存在
* 返回的是Stat:里面有节点的所有信息
* 顺便提一下watch:每个方法其实有两套,一个是boolean watch 一个是Watcher watcher
* 使用第一种:Zookeeper在创建的时候就绑定watcher,使用第二种是使用匿名类
*/
System.out.println("/testRoot是否存在:"+zk.exists("/testRoot", false));
/**
* 删除节点
* 第一个参数:path
* 第二个参数:版本号 -1表示不理睬
* 注意:如果节点下面有子节点,删除是会报错的:KeeperErrorCode = Directory not empty for xxx
*/
//删除父节点时,先获取子节点,然后删除子节点,最后再删除父节点
List<String> sonPaths = zk.getChildren("/testRoot", false);
sonPaths.forEach((path)->{
try {
zk.delete("/testRoot/"+path, -1);
} catch (Exception e) {
e.printStackTrace();
}
});
zk.delete("/testRoot", -1);
System.out.println("/testRoot是否存在:"+zk.exists("/testPath", false));
//最后断开连接
zk.close();
}
}