Java学习+面试指南:https://javaxiaobear.cn
ZooKeeper应用的开发主要通过Java客户端API去连接和操作ZooKeeper集群。可供选择的Java客户端API有:
ZooKeeper官方的Java客户端API。
第三方的Java客户端API,比如Curator。
ZooKeeper官方的客户端API提供了基本的操作。例如,创建会话、创建节点、读取节点、更新数据、删除节点和检查节点是否存在等。不过,对于实际开发来说,ZooKeeper官方API有一些不足之处,具体如下:
ZooKeeper的Watcher监测是一次性的,每次触发之后都需要重新进行注册。
会话超时之后没有实现重连机制。
异常处理烦琐,ZooKeeper提供了很多异常,对于开发人员来说可能根本不知道应该如何处理这些抛出的异常。
仅提供了简单的byte[]数组类型的接口,没有提供Java POJO级别的序列化数据处理接口。
创建节点时如果抛出异常,需要自行检查节点是否存在。
无法实现级联删除。
总之,ZooKeeper官方API功能比较简单,在实际开发过程中比较笨重,一般不推荐使用。
1、创建一个maven项目,引入zookeeper client依赖
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.8.3version>
dependency>
注意:保持与服务端版本一致,不然会有很多兼容性的问题
2、学习API
ZooKeeper原生客户端主要使用org.apache.zookeeper.ZooKeeper这个类来使用ZooKeeper服务。
ZooKeeper常用构造器
ZooKeeper (connectString, sessionTimeout, watcher)
connectString:使用逗号分隔的列表,每个ZooKeeper节点是一个host.port对,host 是机器名或者IP地址,port是ZooKeeper节点对客户端提供服务的端口号。客户端会任意选取connectString 中的一个节点建立连接。
sessionTimeout : session timeout时间。
watcher:用于接收到来自ZooKeeper集群的事件。
使用 zookeeper 原生 API,连接zookeeper集群
3、代码连接客户端
public class ZkClientDemo {
private static final String CONNECT_STR = "localhost:2181";
/**
* 连接地址
*/
private final static String CLUSTER_CONNECT_STR="ip:2181";
public static void main(String[] args) throws Exception {
final CountDownLatch countDownLatch=new CountDownLatch(1);
ZooKeeper zooKeeper = new ZooKeeper(CLUSTER_CONNECT_STR, 4000, watchedEvent -> {
if(Watcher.Event.KeeperState.SyncConnected == watchedEvent.getState()){
//如果收到了服务端的响应事件,连接成功
countDownLatch.countDown();
System.out.println("连接建立");
}
});
System.out.println("连接中");
countDownLatch.await();
//CONNECTED
System.out.println(zooKeeper.getState());
Stat stat = zooKeeper.exists("/zk",false);
if(null ==stat){
//创建持久节点
zooKeeper.create("/zk","javaxiaobear".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
//持久监听
zooKeeper.addWatch("/zk",new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event);
}
},AddWatchMode.PERSISTENT);
Thread.sleep(Integer.MAX_VALUE);
}
}
create(path, data, acl,createMode): 创建一个给定路径的 znode,并在 znode 保存 data[]的 数据,createMode指定 znode 的类型。
delete(path, version):如果给定 path 上的 znode 的版本和给定的 version 匹配, 删除 znode。
exists(path, watch):判断给定 path 上的 znode 是否存在,并在 znode 设置一个 watch。
getData(path, watch):返回给定 path 上的 znode 数据,并在 znode 设置一个 watch。
setData(path, data, version):如果给定 path 上的 znode 的版本和给定的 version 匹配,设置 znode 数据。
getChildren(path, watch):返回给定 path 上的 znode 的孩子 znode 名字,并在 znode 设置一个 watch。
sync(path):把客户端 session 连接节点和 leader 节点进行同步。
方法特点:
所有获取 znode 数据的 API 都可以设置一个 watch 用来监控 znode 的变化。
所有更新 znode 数据的 API 都有两个版本: 无条件更新版本和条件更新版本。如果 version 为 -1,更新为无条件更新。否则只有给定的 version 和 znode 当前的 version 一样,才会进行更新,这样的更新是条件更新。
所有的方法都有同步和异步两个版本。同步版本的方法发送请求给 ZooKeeper 并等待服务器的响 应。异步版本把请求放入客户端的请求队列,然后马上返回。异步版本通过 callback 来接受来 自服务端的响应。
ZooKeeper 类通过其构造函数提供连接功能。构造函数的签名如下:
ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)
代码实现如下:
public class ZkClientDemo {
private static final String CONNECT_STR = "localhost:2181";
/**
* 连接地址
*/
private final static String CLUSTER_CONNECT_STR="ip:2181";
public static void main(String[] args) throws Exception {
final CountDownLatch countDownLatch=new CountDownLatch(1);
ZooKeeper zooKeeper = new ZooKeeper(CLUSTER_CONNECT_STR, 4000, watchedEvent -> {
System.out.println(watchedEvent);
if(Watcher.Event.KeeperState.SyncConnected == watchedEvent.getState()){
//如果收到了服务端的响应事件,连接成功
countDownLatch.countDown();
System.out.println("连接建立");
}
});
System.out.println("连接中");
countDownLatch.await();
//CONNECTED
System.out.println(zooKeeper.getState());
}
}
**注意:**如果连接,请检查一下是否开启了防火墙,服务器2181端口是否放开了
create基础的方法用法如下
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
代码实现如下:
/**
* 创建节点
* @param parentPath 父节点路径
* @param nodeName 节点名称
* @param data 数据
*/
public void createNode(String parentPath, String nodeName, String data) throws Exception{
zooKeeper.create(parentPath + "/" + nodeName ,data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
ZooKeeper类提供了exists方法来检查znode的存在。如果指定的znode存在,它将返回znode的元数据。exists方法的用法如下:
exists(String path, boolean watcher)
参数:
代码实现如下:
/**
* 判断某个节点是否存在
* @param path 节点路径
* @throws Exception 异常
*/
public Stat existsNode(String path) throws Exception{
return zooKeeper.exists(path, true);
}
ZooKeeper类提供了getData方法来获取附加到指定znode中的数据及其状态。getData方法的用法如下:
getData(String path, Watcher watcher, Stat stat)
参数:
代码实现如下:
/**
* 获取节点数据
* @param path 路径
* @return
* @throws Exception
*/
public String getData(String path) throws Exception{
byte[] data = zooKeeper.getData(path, false, null);
return new String(data);
}
ZooKeeper类提供setData方法来修改附加到指定znode中的数据。setData方法的用法如下:
setData(String path, byte[] data, int version)
参数:
代码实现如下:
/**
* 设置节点数据
* @param path 路径
* @param data 数据
* @throws Exception
*/
public void setData(String path, String data) throws Exception{
zooKeeper.setData(path, data.getBytes(), zooKeeper.exists(path, true).getVersion());
}
ZooKeeper类提供了getChildren方法来获取特定znode的所有子节点。getChildren方法的用法如下:
getChildren(String path, Watcher watcher)
参数:
代码实现如下:
/**
* 获取路径下的子节点
* @param path 路径
* @throws Exception
*/
public void getChildren(String path) throws Exception{
//首先判断节点是否存在
Stat exists = zooKeeper.exists(path, true);
if (null != exists){
List<String> children = zooKeeper.getChildren(path, false);
for (String child : children) {
System.out.println(child);
}
}
}
ZooKeeper类提供了delete方法来删除指定的znode。delete方法的用法如下:
delete(String path, int version)
参数:
代码实现如下:
/**
* 删除节点
* @param path
* @throws Exception
*/
public void deleteNode(String path) throws Exception{
zooKeeper.delete(path, -1);
}
/**
* 监听节点
* @param path 路径
* @throws Exception
*/
public void addWatch(String path) throws Exception{
zooKeeper.addWatch(path, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println(watchedEvent.getState());
}
}, AddWatchMode.PERSISTENT);
}
public class ZkBaseOperations {
private static final String ZOOKEEPER_ADDRESS = "ip:2181";
private static final int SESSION_TIMEOUT = 3000;
private ZooKeeper zooKeeper;
public ZkBaseOperations() {
try {
CountDownLatch connectionLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper(ZOOKEEPER_ADDRESS, SESSION_TIMEOUT, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
connectionLatch.countDown();
System.out.println("连接成功!");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭连接
*/
public void close() {
if (zooKeeper != null) {
try {
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 创建节点
* @param parentPath 父节点路径
* @param nodeName 节点名称
* @param data 数据
*/
public void createNode(String parentPath, String nodeName, String data) throws Exception{
zooKeeper.create(parentPath + "/" + nodeName ,data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 判断某个节点是否存在
* @param path 节点路径
* @throws Exception 异常
*/
public Stat existsNode(String path) throws Exception{
return zooKeeper.exists(path, true);
}
/**
* 获取节点数据
* @param path 路径
* @return
* @throws Exception
*/
public String getData(String path) throws Exception{
byte[] data = zooKeeper.getData(path, false, null);
return new String(data);
}
/**
* 设置节点数据
* @param path 路径
* @param data 数据
* @throws Exception
*/
public void setData(String path, String data) throws Exception{
zooKeeper.setData(path, data.getBytes(), zooKeeper.exists(path, true).getVersion());
}
/**
* 获取路径下的子节点
* @param path 路径
* @throws Exception
*/
public void getChildren(String path) throws Exception{
//首先判断节点是否存在
Stat exists = zooKeeper.exists(path, true);
if (null != exists){
List<String> children = zooKeeper.getChildren(path, false);
for (String child : children) {
System.out.println(child);
}
}
}
/**
* 删除节点
* @param path
* @throws Exception
*/
public void deleteNode(String path) throws Exception{
zooKeeper.delete(path, -1);
}
public static void main(String[] args) {
ZkBaseOperations zookeeper = new ZkBaseOperations();
try {
//创建节点
zookeeper.createNode("/javaxiaobear", "666","javaxiaobear is a good website");
//判断阶段是否存在
Stat stat = zookeeper.existsNode("/javaxiaobear/666");
if(stat != null) {
System.out.println("javaxiaobear Node exists and the node version is " +
stat.getVersion());
} else {
System.out.println("Node does not exists");
}
//获取节点数据
String data = zookeeper.getData("/javaxiaobear/666");
System.out.println("获取节点的数据为:" + data);
//设置节点数据
zookeeper.setData("/javaxiaobear", "666");
//获取子节点
zookeeper.getChildren("/javaxiaobear");
//删除节点
zookeeper.deleteNode("/javaxiaobear");
zookeeper.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ZkClient是Github上⼀个开源的zookeeper客户端,在Zookeeper原⽣API接⼝之上进⾏了包装,是⼀个更易⽤的Zookeeper客户端,同时,zkClient在内部还实现了诸如Session超时重连、Watcher反复注册等功能。
**添加依赖:**在pom.xml⽂件中添加如下内容
<dependency>
<groupId>com.101tecgroupId>
<artifactId>zkclientartifactId>
<version>0.11version>
dependency>
使⽤ZkClient可以轻松的创建会话,连接到服务端。
public class CreateSession {
/**
* 连接地址
*/
private final static String CLUSTER_CONNECT_STR="ip:2181";
public static void main(String[] args) {
ZkClient zkClient = new ZkClient(CLUSTER_CONNECT_STR);
System.out.println("zkClient 连接成功");
}
}
运⾏结果:zkClient 连接成功。结果表明已经成功创建会话。
ZkClient提供了递归创建节点的接⼝,即其帮助开发者先完成⽗节点的创建,再创建⼦节点
public class Create_Node_Sample {
public static void main(String[] args) {
ZkClient zkClient = new ZkClient("ip:2181");
//createParents的值设置为true,可以递归创建节点
zkClient.createPersistent("/javaxiaobear",true);
System.out.println("创建节点成功");
}
}
运⾏结果:success create znode.
结果表明已经成功创建了节点,值得注意的是,在原⽣态接⼝中是⽆法创建成功的(⽗节点不存在),但是通过ZkClient通过设置createParents参数为true可以递归的先创建⽗节点,再创建⼦节点
ZkClient提供了递归删除节点的接⼝,即其帮助开发者先删除所有⼦节点(存在),再删除⽗节点。
public class DeleteNode {
public static void main(String[] args) {
ZkClient zkClient = new ZkClient("ip:2181");
zkClient.deleteRecursive("/javaxiaobear");
System.out.println("删除节点成功");
}
}
运⾏结果: 删除节点成功
结果表明ZkClient可直接删除带⼦节点的⽗节点,因为其底层先删除其所有⼦节点,然后再删除⽗节点
public class Get_Children_Sample {
public static void main(String[] args) throws Exception {
ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
List<String> children = zkClient.getChildren("/javaxiaobear");
System.out.println(children);
//注册监听事件
zkClient.subscribeChildChanges(path, new IZkChildListener() {
public void handleChildChange(String parentPath, List<String>
currentChilds) throws Exception {
System.out.println(parentPath + " 's child changed,currentChilds:" + currentChilds);
}
});
zkClient.createPersistent("/javaxiaobear");
Thread.sleep(1000);
zkClient.createPersistent("/javaxiaobear/c1");
Thread.sleep(1000);
zkClient.delete("/javaxiaobear/c1");
Thread.sleep(1000);
zkClient.delete(path);
Thread.sleep(Integer.MAX_VALUE);
}
}
运⾏结果:
/zk-book 's child changed, currentChilds:[]
/zk-book 's child changed, currentChilds:[c1]
/zk-book 's child changed, currentChilds:[]
/zk-book 's child changed, currentChilds:null
结果表明:客户端可以对⼀个不存在的节点进⾏⼦节点变更的监听。⼀旦客户端对⼀个节点注册了⼦节点列表变更监听之后,那么当该节点的⼦节点列表发⽣变更时,服务端都会通知客户端,并将最新的⼦节点列表发送给客户端,该节点本身的创建或删除也会通知到客户端。
public class Get_Data_Sample {
public static void main(String[] args) throws InterruptedException {
String path = "/javaxiaobear-zk";
ZkClient zkClient = new ZkClient("127.0.0.1:2181");
//判断节点是否存在
boolean exists = zkClient.exists(path);
if (!exists){
zkClient.createEphemeral(path, "123");
}
//注册监听
zkClient.subscribeDataChanges(path, new IZkDataListener() {
public void handleDataChange(String path, Object data) throws
Exception {
System.out.println(path+"该节点内容被更新,更新后的内容"+data);
}
public void handleDataDeleted(String s) throws Exception {
System.out.println(s+" 该节点被删除");
}
});
//获取节点内容
Object o = zkClient.readData(path);
System.out.println(o);
//更新
zkClient.writeData(path,"4567");
Thread.sleep(1000);
//删除
zkClient.delete(path);
Thread.sleep(1000);
}
}
运⾏结果:
123
/javaxiaobear-zk该节点内容被更新,更新后的内容4567
/javaxiaobear-zk 该节点被删除
Curator是Netflix公司开源的一套ZooKeeper客户端框架,和ZkClient一样它解决了非常底层的细节开发工作,包括连接、重连、反复注册Watcher的问题以及NodeExistsException异常等。
Curator是Apache基金会的顶级项目之一,Curator具有更加完善的文档,另外还提供了一套易用性和可读性更强的Fluent风格的客户端API框架。
Curator还为ZooKeeper客户端框架提供了一些比较普遍的、开箱即用的、分布式开发用的解决方案,例如Recipe、共享锁服务、Master选举机制和分布式计算器等,帮助开发者避免了“重复造轮子”的无效开发工作。
Guava is to Java that Curator to ZooKeeper
在实际的开发场景中,使用Curator客户端就足以应付日常的ZooKeeper集群操作的需求。
官网:https://curator.apache.org/
引入依赖
Curator 包含了几个包:
curator-framework是对ZooKeeper的底层API的一些封装。
curator-client提供了一些客户端的操作,例如重试策略等。
curator-recipes封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等。
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.8.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>5.1.0version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
exclusion>
exclusions>
dependency>
在使用curator-framework包操作ZooKeeper前,首先要创建一个客户端实例。这是一个CuratorFramework类型的对象,有两种方法:
使用工厂类CuratorFrameworkFactory的静态newClient()方法。
public static CuratorFramework newClient(String connectString,RetryPolicy retryPolicy)
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy)
代码实现如下:
public class CuratorDemo {
private final static String CLUSTER_CONNECT_STR="ip:2181";
public static void main(String[] args) throws Exception {
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
// //创建客户端实例
CuratorFramework client = CuratorFrameworkFactory.newClient(CLUSTER_CONNECT_STR, retryPolicy);
//启动客户端
client.start();
System.out.println("zookeeper初始化连接成功:" + client);
//关闭连接
client.close();
}
}
使用工厂类CuratorFrameworkFactory的静态builder构造者方法。
public class CuratorDemo {
private final static String CLUSTER_CONNECT_STR="ip:2181";
public static void main(String[] args) throws Exception {
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(CLUSTER_CONNECT_STR)
.sessionTimeoutMs(5000) // 会话超时时间
.connectionTimeoutMs(5000) // 连接超时时间
.retryPolicy(retryPolicy)
.namespace("base") // 包含隔离名称
.build();
//启动客户端
client.start();
System.out.println("zookeeper初始化连接成功:" + client);
//关闭连接
client.close();
}
}
connectionString:服务器地址列表,在指定服务器地址列表的时候可以是一个地址,也可以是多个地址。如果是多个地址,那么每个服务器地址列表用逗号分隔, 如 host1:port1,host2:port2,host3;port3 。
retryPolicy:重试策略,当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方式。在 Curator 内部,可以通过判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服务端错误。
策略名称 | 描述 |
---|---|
ExponentialBackoffRetry | 重试一组次数,重试之间的睡眠时间增加 |
RetryNTimes | 重试最大次数 |
RetryOneTime | 只重试一次 |
RetryUntilElapsed | 在给定的时间结束之前重试 |
主要方法有以下:
public T forPath(String path)
//创建节点,并赋值内容
public T forPath(String path, byte[] data)
//判断节点是否存在,节点存在了,创建时仍然会报错
public ExistsBuilder checkExists()
代码实现如下:
/**
* 创建节点
* @param nodePath 路径
* @param data 数据
*/
public void createNode(String nodePath, String data) throws Exception{
if (StringUtils.isEmpty(nodePath)) {
System.out.println("节点【" + nodePath + "】不能为空");
}
//1、对节点是否存在进行判断,否则会报错:【NodeExistsException: KeeperErrorCode = NodeExists for /root】
Stat exists = client.checkExists().forPath(nodePath);
if (null != exists) {
System.out.println("节点【" + nodePath + "】已存在,不能新增");
}
//2、创建节点 永久节点
client.create().forPath(nodePath);
// 2.1 手动指定节点的类型
client.create().withMode(CreateMode.PERSISTENT).forPath(nodePath);
//2.2 如果父节点不存在可创建当前的父节点
String node = client.create().creatingParentsIfNeeded().forPath(nodePath);
System.out.println(node);
//创建节点,并为当前节点赋值内容
if (!StringUtils.isBlank(data)) {
//2.3、创建永久节点,并为当前节点赋值内容
client.create()
.forPath(nodePath, data.getBytes());
//2.4、创建永久有序节点
client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(nodePath, data.getBytes());
//2.5、创建临时节点
client.create()
.withMode(CreateMode.EPHEMERAL)
.forPath(nodePath, data.getBytes());
}
//2.6、创建临时有序节点
client.create()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(nodePath, data.getBytes());
}
在 Curator 中,可以使用 create 函数创建数据节点,并通过 withMode 函数指定节点类型(持久化节点,临时节点,顺序节点,临时顺序节点,持久化顺序节点等),默认是持久化节点,之后调用 forPath 函数来指定节点的路径和数据信息。
主要的方法如下:
//获取某个节点数据
client.getData().forPath(nodePath)
//读取zookeeper的数据,并放到Stat中
client.getData().storingStatIn(stat1).forPath(nodePath)
参数说明:
path:指定要读取的节点
stat:指定数据节点的节点状态信息
我们通过客户端实例的 setData() 方法更新 ZooKeeper 服务上的数据节点,在setData 方法的后边,通过 forPath 函数来指定更新的数据节点路径以及要更新的数据。
/**
* 设置修改节点数据
* @param nodePath 节点路径
* @param data 数据
*/
public void setData(String nodePath, String data) throws Exception{
//更新节点
Stat stat = client.setData().forPath(nodePath, data.getBytes());
// 指定版本号,更新节点,更新的时候如果指定数据版本的话,那么需要和zookeeper中当前数据的版本要一致,-1表示匹配任何版本
Stat stat1 = client.setData().withVersion(-1).forPath(nodePath, data.getBytes());
//异步设置某个节点数据
Stat stat2 = client.setData().inBackground().forPath(nodePath, data.getBytes());
System.out.println(stat1.toString());
}
/**
* 获取某个节点的子节点
* @param nodePath
* @throws Exception
*/
public void getChildren(String nodePath) throws Exception{
List<String> list = client.getChildren().forPath(nodePath);
for (String s : list) {
System.out.println(s);
}
}
/**
* 删除节点
* @param nodePath 节点路径
* @throws Exception
*/
public void delete(String nodePath) throws Exception{
client.delete().forPath(nodePath);
//删除节点,即使出现网络故障,zookeeper也可以保证删除该节点
client.delete().guaranteed().forPath(nodePath);
//级联删除节点(如果当前节点有子节点,子节点也可以一同删除)
client.delete().deletingChildrenIfNeeded().forPath(nodePath);
}
Curator 引入了BackgroundCallback 接口,用来处理服务器端返回来的信息,这个处理过程是在异步线程中调用,默认在 EventThread 中调用,也可以自定义线程池。
public interface BackgroundCallback
{
/**
* Called when the async background operation completes
*
* @param client the client
* @param event operation result details
* @throws Exception errors
*/
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception;
}
如上接口,主要参数为 client 客户端, 和 服务端事件 event ,inBackground 异步处理默认在EventThread中执行
制定线程池异步处理
/**
* 指定线程池 获取节点数据
* @param nodePath
* @throws Exception
*/
public void testExecutorService(String nodePath) throws Exception{
ExecutorService executorService = Executors.newSingleThreadExecutor();
byte[] data = client.getData()
.inBackground((o1, o2) -> {
}, executorService).forPath(nodePath);
System.out.println(new String(data));
}
API方法如下:
/**
* Receives notifications about errors and background events
*/
public interface CuratorListener
{
/**
* Called when a background task has completed or a watch has triggered
*
* @param client client
* @param event the event
* @throws Exception any errors
*/
public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception;
}
针对 background 通知和错误通知。使用此监听器之后,调用inBackground 方法会异步获得监听
Curator Caches
Curator 引入了 Cache 来实现对 Zookeeper 服务端事件监听,Cache 事件监听可以理解为一个本地缓存视图与远程 Zookeeper 视图的对比过程。Cache 提供了反复注册的功能。Cache 分为两类注册类型:节点监听和子节点监听。
node cache:
NodeCache 对某一个节点进行监听
public NodeCache(CuratorFramework client, String path);
可以通过注册监听器来实现,对当前节点数据变化的处理
public void addListener(NodeCacheListener listener)
代码实现:
public class CuratorNodeCacheTest {
private static final String ZOOKEEPER_ADDRESS = "ip:2181";
private static final int SESSION_TIMEOUT = 3000;
public CuratorFramework client;
public CuratorNodeCacheTest() {
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient(ZOOKEEPER_ADDRESS, retryPolicy);
client.start();
}
/**
*
* @param nodePath 路径
*/
public void nodeCache(String nodePath) throws Exception {
NodeCache nodeCache = new NodeCache(client, nodePath);
//监听当前节点路径
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
//查询节点数据
byte[] data = client.getData().forPath(nodePath);
System.out.println(new String(data));
}
});
nodeCache.start();
}
}
path cache:
PathChildrenCache 会对子节点进行监听,但是不会对二级子节点进行监听,
public PathChildrenCache(CuratorFramework client,
String path,
boolean cacheData)
可以通过注册监听器来实现,对当前节点的子节点数据变化的处理
public void addListener(PathChildrenCacheListener listener)
/**
*
* @param nodePath 节点路径
* @throws Exception
*/
public void pathCache(String nodePath) throws Exception{
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, nodePath, true);
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println(event);
}
});
// 如果设置为true则在首次启动时就会缓存节点内容到Cache中
pathChildrenCache.start(true);
}
tree cache:
TreeCache 使用一个内部类TreeNode来维护这个一个树结构。并将这个树结构与ZK节点进行了映射。所以TreeCache 可以监听当前节点下所有节点的事件。
public TreeCache(CuratorFramework client, String path, boolean cacheData)
可以通过注册监听器来实现,对当前节点的子节点,及递归子节点数据变化的处理
public void addListener(TreeCacheListener listener)
public void treeCache(String nodePath) throws Exception{
TreeCache treeCache = new TreeCache(client, nodePath);
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
System.out.println(event);
}
});
treeCache.start();
}