znode是zooKeeper集合的核心组件,zookeeper API提供了一小组方法使用 zookeeper集合来操纵znode的所有细节。
客户端应该遵循以步骤,与zookeeper服务器进行清晰和干净的交互。
- 连接到zookeeper服务器,zookeeper服务器为客户端分配会话ID(sessionId)。
- 定期向服务器发送心跳。否则,zookeeper服务器将过期会话ID,客户端需要重新连接。
- 只要会话ID处于活动状态,就可以获取/设置znode。
- 所有任务完成后,断开与zookeeper服务器的连接。如果客户端长时间不活动,则zookeeper服务器将自动断开客户端。
maven依赖
<dependencies>
<!--zookeeper核心依赖-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.2</version>
</dependency>
<!--junit测试工具依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
zookeeper API
- ZooKeeper构造函数
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher);
- connectString:连接到zookeeper主机,格式:ip + port。
- sessionTimeout:会话超时时长,单位毫秒。
- watcher:监视器对象。由于zookeeper API连接zookeeper服务是异步连接的,所以通过监控器来监听连接服务状态。
package com.huazai.zookeeper.zkexample.config;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
/**
* @author pyh
* @date 2021/5/19 0:20
*/
public class ZookeeperConnection {
public static void main(String[] args) {
connect();
}
public static ZooKeeper zooKeeper;
public static ZooKeeper connect() {
try {
// 由于连接zookeeper服务器是异步连接,需要CountDownLatch阻塞主线程,等待子线程连接结果后反馈给主线程
CountDownLatch countDownLatch = new CountDownLatch(1);
/*
connectString:服务器的ip和端口
sessionTimeout:客户端与服务器之间的会话超时时间,以毫秒为单位的
watcher:监视器对象
*/
zooKeeper = new ZooKeeper("192.168.64.129:2181", 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("zookeeper异步连接成功");
} else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
System.out.println("断开连接");
} else if (watchedEvent.getState() == Event.KeeperState.Expired) {
System.out.println("会话超时");
} else if (watchedEvent.getState() == Event.KeeperState.AuthFailed) {
System.out.println("认证失败");
} else if (watchedEvent.getState() == Event.KeeperState.Closed) {
System.out.println("连接关闭");
}
countDownLatch.countDown();
}
});
// 主线程阻塞等待连接对象的创建成功
countDownLatch.await();
// 会话编号
System.out.println("客户端sessionId:" + zooKeeper.getSessionId());
return zooKeeper;
} catch (Exception e) {
System.out.println("zookeeper连接异常");
e.printStackTrace();
}
return null;
}
/**
* 关闭zookeeper会话
*/
public static void close() {
if (zooKeeper != null) {
try {
zooKeeper.close();
System.out.println("zookeeper关闭成功");
} catch (InterruptedException e) {
System.out.println("zookeeper关闭失败");
e.printStackTrace();
}
}
}
}
- create新增节点方法
// 同步方法
public String create(final String path, byte[] data, List<ACL> acl, CreateMode createMode);
// 异步方法
public void create(final String path, byte[] data, List<ACL> acl, CreateMode createMode, StringCallback cb, Object ctx);
- path:znode路径。例如,/node1 /node11/node111。
- data:要存储在指定znode路径中的数据。
- acl:要创建的节点的访问控制列表。zookeeper API提供了一个静态接口 ZooDefs.Ids 来获取一些基本的acl列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE 返回开放的znode的acl列表。
- createMode:节点的类型,这是一个枚举。
- callBack:异步回调接口。
- ctx:传递上下文参数。
一共有create六个重载方法,其中带StringCallback形参的为异步新增节点方法,不带的为同步新增节点方法,其中三个为同步方法,三个异步方法。同步新增节点的重载方法返回值为成功创建节点后的路径。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author pyh
* @date 2021/5/19 0:53
*/
public class ZKCreate {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试同步新增节点方法
*
* @throws Exception
*/
@Test
public void createSync() throws Exception {
/*
path:节点的路径
data:节点的数据
acl:权限列表 world:anyone:cdrwa
createMode:节点类型,持久化节点
*/
String nodePath = zooKeeper.create("/createSync", "createSync".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(nodePath); // /createSync
}
/**
* 测试新增节点的自定义world授权模式权限列表
*
* @throws Exception
*/
@Test
public void createCustomAclForWorld() throws Exception {
// 权限列表集合
List<ACL> acls = new ArrayList<>();
// 入参分别为授权模式和授权对象
Id id = new Id("world", "anyone");
// 权限列表
acls.add(new ACL(ZooDefs.Perms.READ, id)); // world:anyone:r
acls.add(new ACL(ZooDefs.Perms.WRITE, id)); // world:anyone:w
String nodePath = zooKeeper.create("/createCustomAclForWorld", "createCustomAclForWorld".getBytes(), acls, CreateMode.PERSISTENT);
System.out.println(nodePath); // /createCustomAclForWorld
}
/**
* 测试新增节点的自定义ip授权模式权限列表
*
* @throws Exception
*/
@Test
public void createCustomAclForIp() throws Exception {
// 权限列表集合
List<ACL> acls = new ArrayList<>();
// 入参分别为授权模式和授权对象
Id id = new Id("ip", "192.168.64.129");
// 权限列表
acls.add(new ACL(ZooDefs.Perms.ALL, id)); // ip:192.168.64.129:crdwa
acls.add(new ACL(ZooDefs.Perms.WRITE, id)); // ip:192.168.64.129:w
String nodePath = zooKeeper.create("/createCustomAclForId", "createCustomAclForId".getBytes(), acls, CreateMode.PERSISTENT);
System.out.println(nodePath); // /createCustomAclForWorld
}
/**
* 测试新增节点的auth授权模式权限列表
*
* @throws Exception
*/
@Test
public void createAuth() throws Exception {
// 添加授权用户
zooKeeper.addAuthInfo("digest", "huazai:123456".getBytes()); // addauth digest huazai:123456
/*
若应用auth授权模式,必须使用CREATOR_ALL_ACL常量,不能使用OPEN_ACL_UNSAFE,
因为CREATOR_ALL_ACL指定的是授权为auth,OPEN_ACL_UNSAFE指向的授权模式是world,底层源码中可以看到
*/
String nodePath = zooKeeper.create("/createAuth", "createAuth".getBytes(),
ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
System.out.println(nodePath);
}
/**
* 测试新增节点的自定义auth授权模式权限列表
*
* @throws Exception
*/
@Test
public void createCustomAclForAuth() throws Exception {
// 添加授权用户
zooKeeper.addAuthInfo("digest", "huazai:123456".getBytes()); // addauth digest huazai:123456
// 权限列表
List<ACL> acls = new ArrayList<ACL>();
// 授权模式和授权对象
Id id = new Id("auth", "huazai");
// 权限设置
acls.add(new ACL(ZooDefs.Perms.READ, id)); // auth:huazai:r
String nodePath = zooKeeper.create("/createCustomAclForAuth", "createCustomAclForAuth".getBytes(), acls, CreateMode.PERSISTENT);
System.out.println(nodePath);
}
/**
* 测试新增节点的自定义digest授权模式权限列表
*
* @throws Exception
*/
@Test
public void createCustomAclForDigest() throws Exception {
// 权限列表
List<ACL> acls = new ArrayList<ACL>();
// 授权模式和授权对象
// 密文生成格式:echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
Id id = new Id("digest", "huazai:gb3+BhkHKjQhm0YiyONAAoQUKpc=");
// 权限设置
acls.add(new ACL(ZooDefs.Perms.ALL, id)); // digest:huazai:123456:crdwa
String nodePath = zooKeeper.create("/createCustomAclForDigest", "createCustomAclForDigest".getBytes(), acls, CreateMode.PERSISTENT);
System.out.println(nodePath);
}
/**
* 测试新增持久化有序节点
*
* @throws Exception
*/
@Test
public void createPersistenSequential() throws Exception {
// 创建节点同时指定权限,
// 等价于:create -s /createPersistenSequential "createPersistenSequential" "world:anyone:crdwa"
String nodePath = zooKeeper.create("/createPersistenSequential", "createPersistenSequential".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
// 成功创建后节点路径为带序号的节点路径,而不是自己指定的路径
System.out.println(nodePath);
}
/**
* 测试新增临时节点
*
* @throws Exception
*/
@Test
public void createEphemeral() throws Exception {
// 创建节点同时指定权限,
// 等价于:create -e /createEphemeral "createEphemeral" "world:anyone:crdwa"
String nodePath = zooKeeper.create("/createEphemeral", "createEphemeral".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
// 由于创建的是临时节点,只在当前会话有效,所以创建之后自己到虚拟机是查询不到该节点数据
System.out.println(nodePath);
}
/**
* 测试新增临时有序节点
*
* @throws Exception
*/
@Test
public void createEphemeralSequential() throws Exception {
// 创建节点同时指定权限,
// 等价于:create -e -s /createEphemeral "createEphemeral" "world:anyone:crdwa"
String nodePath = zooKeeper.create("/createEphemeralSequential", "createEphemeralSequential".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// 由于创建的是临时节点,只在当前会话有效,所以创建之后自己到虚拟机是查询不到该节点数据
System.out.println(nodePath);
}
/**
* 测试异步新增节点
*
* @throws Exception
*/
@Test
public void createAsync() throws Exception {
zooKeeper.create("/createAsync", "createAsync".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,
new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
// 0 代表创建成功,状态码参考:org.apache.zookeeper.KeeperException.CodeDeprecated
System.out.println(rc); // 0
// 节点的路径
System.out.println(path); // /createAsync
// 节点路径
System.out.println(name); // /createAsync
// 上下文对象
System.out.println(ctx); // 我是上下文对象,在processResult回调方法中的ctx参数可以获取到我
}
}, "我是上下文对象,在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}
- set设置节点数据
// 同步设置数据方法
public Stat setData(final String path, byte[] data, int version);
// 异步设置数据方法
public void setData(final String path, byte[] data, int version, StatCallback cb, Object ctx)
- path:znode路径。
- data:要存储在指定znode路径中的数据。
- version:znode的当前版本,-1代表删除节点时不考虑版本信息。
- cb:异步回调接口。
- ctx:传递上下文参数。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author pyh
* @date 2021/5/21 0:18
*/
public class ZKSet {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试同步设置节点数据,如果乐观锁版本不匹配,则修改失败,抛出异常
*/
@Test
public void setSync() throws Exception {
// path:节点的路径
// data:节点数据
// version:乐观锁版本,-1代表版本号不作为修改条件
// set /setSync "test" -v 1
Stat stat = zooKeeper.setData("/setSync", "test".getBytes(), 1);
// 返回值数据等价于:stat /setSync
System.out.println(stat);
}
/**
* 测试异步设置节点数据,如果乐观锁版本不匹配,则修改失败,抛出异常
*
* @throws Exception
*/
@Test
public void setASync() throws Exception {
// set /setASync "test" -v 1
zooKeeper.setData("/setASync", "test".getBytes(), 0,
new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
// 0 代表创建成功,状态码参考:org.apache.zookeeper.KeeperException.CodeDeprecated
System.out.println(rc); // 0
// 节点的路径
System.out.println(path); // /setASync
// 上下文对象
System.out.println(ctx); // 我是上下文对象,在processResult回调方法中的ctx参数可以获取到我
System.out.println(stat); // stat /setASync
}
}, "我是上下文对象,在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}
- delete删除节点
// 同步删除节点方法
public void delete(final String path, int version);
// 异步删除节点方法
public void delete(final String path, int version, VoidCallback cb, Object ctx);
- path:znode路径。
- version:znode的当前版本,-1代表删除节点时不考虑版本信息。
- cb:异步回调接口。
- ctx:传递上下文参数。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author pyh
* @date 2021/5/24 23:50
*/
public class ZKDelete {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试同步删除节点方法
*
* @throws Exception
*/
@Test
public void deleteSync() throws Exception {
zooKeeper.delete("/hello", 0);
}
/**
* 测试异步删除节点方法
*
* @throws Exception
*/
@Test
public void deleteAsync() throws Exception {
zooKeeper.delete("/zk", 0, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
// 0 代表创建成功,状态码参考:org.apache.zookeeper.KeeperException.CodeDeprecated
System.out.println(rc); // 0
// 节点的路径
System.out.println(path); // /hello
// 上下文对象
System.out.println(ctx); // 我是上下文对象,在processResult回调方法中的ctx参数可以获取到我
}
}, "我是上下文对象,在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}
- get获取节点数据
// 同步获取节点数据方法
public byte[] getData(String path, boolean watch, Stat stat);
public byte[] getData(final String path, Watcher watcher, Stat stat);
// 异步获取节点数据方法
public void getData(String path, boolean watch, DataCallback cb, Object ctx);
public void getData(final String path, Watcher watcher, DataCallback cb, Object ctx);
- path:znode路径。
- watch:是否使用连接对象中注册的监视器。
- watcher:监视器对象。
- stat:返回znode的元数据。
- cb:异步回调接口。
- ctx:传递上下文参数。
zookeeper无论同步或者异步方法都提供能拿到监视器对象的重载方法。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author pyh
* @date 2021/5/25 0:06
*/
public class ZKGet {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试同步获取节点数据方法
*
* @throws Exception
*/
@Test
public void getSync() throws Exception {
// 获取的节点属性保存在这里
Stat stat = new Stat();
// 获取节点数据
byte[] data = zooKeeper.getData("/hadoop", false, stat);
System.out.println(new String(data));
System.out.println(stat);
}
/**
* 测试异步获取数据方法
*
* @throws Exception
*/
@Test
public void getAsync() throws Exception {
zooKeeper.getData("/hadoop", false, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
if (rc == 0) {
// 成功获取节点/hadoop数据:hadoop
System.out.println("成功获取节点" + path + "数据:" + new String(data));
}
System.out.println(ctx);
System.out.println(stat);
}
}, "我是上下文对象,在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}
- getChildren查询子节点数据方法
// 获取子节点同步方法,返回值是子节点路径列表
public List<String> getChildren(String path, boolean watch);
// 获取子节点异步方法
public void getChildren(String path, boolean watch, ChildrenCallback cb, Object ctx);
- path:znode父节点路径。
- watch:是否使用连接对象中注册的监视器。
- cb:异步回调接口。
- ctx:传递上下文参数。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* @author pyh
* @date 2021/5/25 0:31
*/
public class getChildren {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试获取子节点同步方法
*
* @throws Exception
*/
@Test
public void getChildrenSync() throws Exception {
// 子节点路径
List<String> list = zooKeeper.getChildren("/hadoop", false);
for (String str : list) {
System.out.println(str);
}
}
/**
* 测试获取子节点异步方法
*
* @throws Exception
*/
@Test
public void getChildrenAsync() throws Exception {
zooKeeper.getChildren("/hadoop", false, new AsyncCallback.ChildrenCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List<String> children) {
if (rc == 0) {
// 成功获取节点/hadoop子节点,子节点数量:1
System.out.println("成功获取节点" + path + "子节点,子节点数量:" + children.size());
}
System.out.println(ctx);
}
}, "在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}
- exists判断节点是否存在方法
// 同步判断方法
public Stat exists(String path, boolean watch);
// 异步判断方法
public void exists(String path, boolean watch, StatCallback cb, Object ctx);
- path:znode路径。
- watch:是否使用连接对象中注册的监视器。
- cb:异步回调接口。
- ctx:传递上下文参数。
package com.huazai.zookeeper.zkexample;
import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author pyh
* @date 2021/5/25 0:28
*/
public class ZKExists {
private ZooKeeper zooKeeper;
@Before
public void before() {
zooKeeper = ZookeeperConnection.connect();
}
/**
* 测试检测节点是否存在同步方法
*
* @throws Exception
*/
@Test
public void existsSync() throws Exception {
Stat stat = zooKeeper.exists("/hadoop", false);
System.out.println(stat);
}
/**
* 测试检测节点是否存在异步方法
*
* @throws Exception
*/
@Test
public void existsAsync() throws Exception {
zooKeeper.exists("/hadoop", false, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println(rc);
// /hadoop
System.out.println(path);
// 我是上下文对象,在processResult回调方法中的ctx参数可以获取到我
System.out.println(ctx);
System.out.println(stat);
}
}, "我是上下文对象,在processResult回调方法中的ctx参数可以获取到我");
}
@After
public void after() {
ZookeeperConnection.close();
}
}