概念
ZooKeeper允许用户在指定节点上注册些Watcher, 并且在一些特定事件触发的时候, ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。
- 比如客户端app1,在节点app1上注册了一个监听器,监听这个节点,
- 这个节点一旦变化了
- 因为客户端app1注册了监听,所以感兴趣,所有Zookeeper会将这个事件通知给客户端app1,而app2、app3是不会收到对应的通知的
ZooKeeper引入了Watcher机制来实现了发布/订阅功能能,能够让多个订阅者同时监听某一个对象, 当一个对象自身状态变化时,会通知所有订阅者。
- 如果三个客户端app1、2、3监听了节点app1
- 当节点app1被客户端app1改变之后,Zookeeper会把改变的数据和改变的事实告诉所有的监听者(即客户端app1、2、3)
- 改变之后,其他的客户端会收到改变的值,并且告诉自己的程序里面去替换这些信息
- 这些改动都是自动的
ZooKeeper原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便
需要开发人员自反复注册Watcher,比较繁琐。
Curator引入了Cache来实现对ZooKeeper服务端事件的监听。
ZooKeeper提供了三种Watcher:
NodeCache
: 只是监听某一个特定的节点PathChildrenCache
:监控一个ZNode的子节点(不包括当前节点)TreeCache
:可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCache的组合(不仅监听这个节点,而且还会监听这个节点下面的儿子,这个所有就是当前节点下面的所有)
演示
NodeCache
public class CuratorWatcherTest {
private CuratorFramework client;
/**
* 建立连接
*/
@Before
public void testConnect() {
/*
*
* @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
* @param sessionTimeoutMs 会话超时时间 单位ms
* @param connectionTimeoutMs 连接超时时间 单位ms
* @param retryPolicy 重试策略
*/
/* //重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
//1.第一种方式
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
60 * 1000, 15 * 1000, retryPolicy);*/
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//2.第二种方式
//CuratorFrameworkFactory.builder();
client = CuratorFrameworkFactory.builder()
.connectString("192.168.149.135:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
//开启连接
client.start();
}
@After
public void close() {
if (client != null) {
client.close();
}
}
/**
* 演示 NodeCache:给指定一个节点注册监听器
*/
@Test
public void testNodeCache() throws Exception {
//1. 创建NodeCache对象,监听app1,因为有了名称空间,所以解/app1
final NodeCache nodeCache = new NodeCache(client,"/app1");
//2. 注册监听
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("节点变化了~");
//获取修改节点后的数据
//修改之后获取修改之后的数据
byte[] data = nodeCache.getCurrentData().getData();
System.out.println(new String(data));
}
});
//3. 开启监听.如果设置为true,则开启监听时,加载缓冲数据
//会在方法调用之前,会将缓存的一些节点的数据都一次性加载过来,有没有都可以
nodeCache.start(true);
//保证不错误,是为了使其一直存在
while (true){
}
//设置完之后,只要修改就会打印上面的信息
}
}
PathChildrenCache
public class CuratorWatcherTest {
private CuratorFramework client;
/**
* 建立连接
*/
@Before
public void testConnect() {
/*
*
* @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
* @param sessionTimeoutMs 会话超时时间 单位ms
* @param connectionTimeoutMs 连接超时时间 单位ms
* @param retryPolicy 重试策略
*/
/* //重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
//1.第一种方式
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
60 * 1000, 15 * 1000, retryPolicy);*/
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//2.第二种方式
//CuratorFrameworkFactory.builder();
client = CuratorFrameworkFactory.builder()
.connectString("192.168.149.135:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
//开启连接
client.start();
}
@After
public void close() {
if (client != null) {
client.close();
}
}
/**
* 演示 PathChildrenCache:监听某个节点的所有子节点们(不包括当前节点)
*/
@Test
public void testPathChildrenCache() throws Exception {
//1.创建监听对象
//监听app2,缓存状态信息
PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);
//2. 绑定监听器
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println("子节点变化了~");
System.out.println(event);//事件对象,获取的是改变的数据内容,包含是什么操作,修改后的数据等
//监听子节点的数据变更,并且拿到变更后的数据
//1.获取类型
PathChildrenCacheEvent.Type type = event.getType();
//2.判断类型是否是update
if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
System.out.println("数据变了!!!");
//获取数据
byte[] data = event.getData().getData();
System.out.println(new String(data));
}
}
});
//3. 开启
pathChildrenCache.start();
while (true){
}
}
}
TreeCache
public class CuratorWatcherTest {
private CuratorFramework client;
/**
* 建立连接
*/
@Before
public void testConnect() {
/*
*
* @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
* @param sessionTimeoutMs 会话超时时间 单位ms
* @param connectionTimeoutMs 连接超时时间 单位ms
* @param retryPolicy 重试策略
*/
/* //重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
//1.第一种方式
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
60 * 1000, 15 * 1000, retryPolicy);*/
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//2.第二种方式
//CuratorFrameworkFactory.builder();
client = CuratorFrameworkFactory.builder()
.connectString("192.168.149.135:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
//开启连接
client.start();
}
@After
public void close() {
if (client != null) {
client.close();
}
}
/**
* 演示 TreeCache:监听某个节点自己和所有子节点们
*/
@Test
public void testTreeCache() throws Exception {
//1. 创建监听器
TreeCache treeCache = new TreeCache(client,"/app2");
//2. 注册监听
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
System.out.println("节点变化了");
System.out.println(event);
}
});
//3. 开启
treeCache.start();
while (true){
}
}
}