znode是Zookeeper集合的核心组件,Zookeeper api提供了一小组方法使用Zookeeper集合来操纵znode的所有细节。这里没有涉及watch相关的api,另外介绍。
客户端应该遵循以下步骤,以保证与Zookeeper服务器进行清晰干净的交互。
- 连接到Zookeeper服务器。Zookeeper服务器为客户端分配会话ID。
- 定期向服务器发送心跳。否则Zookeeper服务器将过期会话ID。客户端需要重新连接。
- 只要会话ID处于活跃状态,就可以操作znode。
- 所以任务完成后,断开与Zookeeper服务器的连接。如果客户端长期不活动,则Zookeeper服务器将自动断开客户端。
需要导入依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
demo代码,每种操作都要同步异步两种模式。
public class ZookeeperDemo implements Watcher{
private ZooKeeper zooKeeper;
//路径前缀
private static final String PATH_PREFIX = "/javaapi";
//相当于发令枪,会阻塞线程,待得Count变为0时,就唤醒线程。
CountDownLatch latch = new CountDownLatch(1);
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState().equals(Event.KeeperState.SyncConnected)){
//如果客户端已经连接到Zookeeper服务器,就会触发SyncConnected事件,进入这里。
//发令器减1 变为0 被该发令器阻塞的线程将被唤醒
latch.countDown();
System.out.println("已连接到客户端");
}
if (watchedEvent.getState().equals(Event.KeeperState.Expired)){
//latch.countDown();
System.out.println("连接超时");
}
}
@Before
public void init(){
try {
//创建一个Zookeeper客户端并连接到服务器,因为连接过程是异步执行的,所以要配合CountDownLatch保证在发送请求前客户端
//与服务器已经建立了连接
//第一个参数:要连接到Zookeeper服务器地址。 第二个参数,session超时,单位毫秒。 第三个参数,为该客户端注册监听。
zooKeeper = new ZooKeeper("192.168.18.130:2181",5000,this);
//阻塞线程。CountDownLatch减为0时就会唤醒线程
latch.await();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@After
public void destroy(){
try {
//关闭连接
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 同步创建节点
*/
@Test
public void testAddNodeSync(){
try {
//第一个参数 创建节点的路径
//第二个参数 节点的数据 ,是一个字节数组
//第三个参数 节点的ACL list列表 ZooDefs.Ids.READ_ACL_UNSAFE代表 world:anyone:adcwr ,后面会详解。
//第四个参数 节点的类型
//返回值是创建的节点的路径
//创建持久化节点
String result = zooKeeper.create(PATH_PREFIX + "/PersistentNode","PersistentNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(result);
//创建持久化有序节点
zooKeeper.create(PATH_PREFIX + "/PersistentSeqNode","PersistentSeqNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
//创建临时节点
zooKeeper.create(PATH_PREFIX + "/EPHEMERALNode","EPHEMERALNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.EPHEMERAL);
//创建临时有序节点
zooKeeper.create(PATH_PREFIX + "/EPHEMERALSeqNode","EPHEMERALSeqNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 异步创建节点
*/
@Test
public void testAddNodeAsync(){
//第一个参数 创建节点的路径
//第二个参数 节点的数据 ,是一个字节数组
//第三个参数 节点的ACL ZooDefs.Ids.READ_ACL_UNSAFE代表 world:anyone:adcwr ,后面会详解。
//第四个参数 节点的类型
//第五个参数 节点创建完成(无论成功还是失败)的回调。
//上下文ctx
zooKeeper.create(PATH_PREFIX + "/asyncNode", "asyncNode".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE,
CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
//结果标志,为0时代表节点创建成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数,就是该create方法传入的最后一个参数
System.out.println(ctx);
//节点的路径。当创建节点失败时,为null。
System.out.println(name);
}
},"aa");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//创建节点时赋予特定的ACL
@Test
public void testCreateNodeWithACL(){
//创建ACL访问控制列表
//第一个参数是权限 1代表 read 2 代表 write 4代表 create 8代表 delete 16代表 admin 相互组合用相加 比如1+2=3 是rw
//第二个参数 Id对象代表授权模型和授权对象 ,构造函数第一个是授权模型 world、ip、auth、digest 第二个参数是授权对象
//world:anyone:r
ACL aclWorld = new ACL(1,new Id("world","anyone"));
//digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:adcwr
ACL aclDigest= new ACL(1+2+4+8+16,new Id("digest","admin:0uek/hZ/V9fgiM35b0Z2226acMQ="));
//ip:192.168.18.128:rw
ACL aclIP = new ACL(1+2,new Id("ip","192.168.18.128"));
//把ACL添加到acl列表中,这个就是创建节点要传递的参数
List<ACL> acls = new ArrayList<>();
acls.add(aclWorld);
acls.add(aclDigest);
acls.add(aclIP);
try {
zooKeeper.create(PATH_PREFIX + "/nodeACL","nodeACL".getBytes(),
acls,CreateMode.PERSISTENT);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//同步修改节点数据
@Test
public void testSetNode(){
try {
//第一个参数 修改的节点路径
//第二个参数 修改的节点数据
//数据的版本号 ,用于实现乐观锁的,如果此版本号与Zookeeper服务器中的该节点数据的版本号不一样,就会修改失败
//返回的是修改后节点的元信息,用Stat类封装,与命令 stat path 返回的一致。-1表示该参数不参与修改节点
Stat stat = zooKeeper.setData(PATH_PREFIX + "/nodeSetData","nodeSetData".getBytes(),-1);
System.out.println(stat.getVersion());
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//修改节点ACL
@Test
public void testSetNodeACL(){
ACL aclWorld = new ACL(1,new Id("world","anyone"));
//digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:adcwr
ACL aclDigest= new ACL(1+2+4+8+16,new Id("digest","admin:0uek/hZ/V9fgiM35b0Z2226acMQ="));
//ip:192.168.18.128:rw
ACL aclIP = new ACL(1+2,new Id("ip","192.168.18.128"));
List<ACL> acls = new ArrayList<>();
acls.add(aclWorld);
acls.add(aclDigest);
acls.add(aclIP);
try {
//第一个参数,节点路径
//第二个参数:要修改的ACL
//第三个参数aVersion -1则表示该参数不参与
zooKeeper.setACL(PATH_PREFIX + "/setACL",acls,-1);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//删除节点
@Test
public void testDeleteNode(){
try {
//第一个参数:要删除的节点路径
//第二个参数数据版本,-1表示不参与。
zooKeeper.delete(PATH_PREFIX + "/nodeDelete",-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
//查看节点数据
@Test
public void getData(){
Stat stat = new Stat();
try {
//第一个参数是节点路径
//第二个参数是是否注册监听,true的话就会注册创建Zookeeper客户端时传入的监听,第三个是传入stat对象,会把获取到的节点元信息赋值到该对象中。
//返回值是节点数据的字节数组
byte[] result = zooKeeper.getData(PATH_PREFIX + "/nodeSetData",false,stat);
System.out.println(new String(result,"utf-8"));
System.out.println(stat.getVersion());
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//获取子节点
@Test
public void getChildrenNode(){
try {
//第一个参数:节点路径
//第二个参数:是否注册监听
//返回值 节点路径列表list。
List<String> children = zooKeeper.getChildren(PATH_PREFIX, false);
System.out.println(children);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//查看节点是否存在
@Test
public void testNodeExist(){
try {
//参数1:节点路径
//第二个参数:是否注册监听
//返回值 stat对象,节点的元数据,如果节点不存在就返回null
Stat exists = zooKeeper.exists("/888", false);
System.out.println(exists);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}