zookeeper功能介绍(三)---java在zookeeper节点上注册监听器
前言:前面2节讲述用java在zookeeper上添加4种节点,以及在节点上存储及读取数据。zookeeper还有一个十分重要的功能是注册监听器。通过注册监听器,当zookeeper节点发生变化时,zookeeper会主动通知客户端,从而实现一些功能。好比如当一台服务器启动的时候,我们在zookeeper上创建一个临时节点。通过监听这些临时节点,我们就可以知道目前有多少台服务器在线。当服务...
前言:
前面2节讲述用java在zookeeper上添加4种节点,以及在节点上存储及读取数据。zookeeper还有一个十分重要的功能是注册监听器。
通过注册监听器,当zookeeper节点发生变化时,zookeeper会主动通知客户端,从而实现一些功能。好比如当一台服务器启动的时候,我们在zookeeper上创建一个临时节点。通过监听这些临时节点,我们就可以知道目前有多少台服务器在线。当服务器关掉时zookeeper也会主动通知我们,这样我们就相当于实时了解当前服务器在线情况,方便协调服务器访问。
下面讲述java如何在zookeeper上注册监听器。
一、监听节点数据变化
监听节点的数据变化事件包括:1、节点被创建; 2、节点上写入数据; 3、节点数据变化; 4、节点数据被删除; 5、节点本身被删除。
以上5种事件都会触发监听器。下面看代码示例:
首先添加maven依赖包,并启动好zookeeper。
maven---pom---dependency:
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
ZookeeperSubscribeDataChanges.java:
package com.lan.test;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
public class ZookeeperSubscribeDataChanges {
private static String zkServer = "192.168.9.188:2181";//zookeeper地址
public static void main(String[] args) {
new ZookeeperSubscribeDataChanges().subscribeDataChanges();
}
private void subscribeDataChanges() {
ZkClient zkClient = new ZkClient(zkServer);//创建zookeeper的java客户端连接
if(!zkClient.exists("/LAN")) {
zkClient.createPersistent("/LAN");
}
//注册监听事件
zkClient.subscribeDataChanges("/LAN/Node", new IZkDataListener() {
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("DataDeleted:"+dataPath);
}
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("DataChange:"+dataPath+",data:"+data);
}
});
System.out.println("****************************************");
zkClient.createPersistent("/LAN/Node");
sleep(100);
zkClient.writeData("/LAN/Node", 1);
sleep(100);
zkClient.writeData("/LAN/Node", 2);
sleep(100);
zkClient.writeData("/LAN/Node", 3);
sleep(100);
zkClient.writeData("/LAN/Node", 4);
sleep(100);
zkClient.writeData("/LAN/Node", 5);
sleep(100);
zkClient.writeData("/LAN/Node", null);
sleep(100);
zkClient.delete("/LAN/Node");
sleep(2000);
zkClient.unsubscribeAll();
zkClient.close();
}
private static void sleep(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1、zkClient.subscribeDataChanges()方法创建一个监听器,监听/LAN/Node节点;
2、创建/LAN/Node节点;
3、向节点写入数据1、2、3、4、5(即修改数据);
4、向节点写入空值(也被认为是修改数据);
5、删除节点。
运行结果如下:
可以看出当修改节点上的数据时(zookeeper把创建节点、写入数据、更新数据、写空值都当做是数据修改),会触发监听器的handleDataChange方法。当删除掉节点时会触发监听器的handleDataDeleted方法。
奇特之处(一定要注意):
根据上面的结果,我们是否可以认为这个监听事件是十分可靠的呢?实践证明并不那么可靠,稍微修改代码,注释掉一些sleep。修改部分代码如下:
System.out.println("****************************************");
zkClient.createPersistent("/LAN/Node");
// sleep(100);
zkClient.writeData("/LAN/Node", 1);
// sleep(100);
zkClient.writeData("/LAN/Node", 2);
// sleep(100);
zkClient.writeData("/LAN/Node", 3);
// sleep(100);
zkClient.writeData("/LAN/Node", 4);
// sleep(100);
zkClient.writeData("/LAN/Node", 5);
// sleep(100);
zkClient.writeData("/LAN/Node", null);
// sleep(100);
zkClient.delete("/LAN/Node");
sleep(2000);
zkClient.unsubscribeAll();
zkClient.close();
运行结果:
可以看出结果很不一样,并且同样的代码,多次运行,结果也不相同。因此这个注册的监听事件不完全可靠。在频繁对节点进行操作时,事件通知并没有一一对应。因此使用时不能按照常规逻辑来写这个代码,一定要在逻辑上确保代码的正确正常运行(本文不讲述这个问题)。
二、监听子节点变化
监听子节点变化包括:1、创建监听的节点; 2、对监听节点添加子节点; 3、删除子节点; 4、删除监听的节点本身。
不包括对子节点的子节点的操作。也就是说这个监听事件包括对本身节点和直接子节点的增删操作。
要注意的是,监听功能可以对当前不存在的节点进行监听。
下面看一段演示代码:
ZookeeperSubscribeChildChanges.java
package com.lan.test;
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class ZookeeperSubscribeChildChanges {
private static String zkServer = "192.168.9.188:2181";//zookeeper地址
public static void main(String[] args) {
new ZookeeperSubscribeChildChanges().subscribeChildChanges();
}
private void subscribeChildChanges() {
ZkClient zkClient = new ZkClient(zkServer);//创建zookeeper的java客户端连接
if(!zkClient.exists("/LAN")) {
zkClient.createPersistent("/LAN");
}
zkClient.subscribeChildChanges("/LAN/Node", new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
String childs = "";
if(currentChilds!=null && currentChilds.size()>0) {
childs += "[";
for(String s: currentChilds) {
childs += s+",";
}
childs += "]";
}
System.out.println("ChildChange:"+parentPath+",childs:"+childs);
}
});
zkClient.createPersistent("/LAN/Node");
sleep(100);
zkClient.createPersistent("/LAN/Node/n1");
sleep(100);
zkClient.createPersistent("/LAN/Node/n2");
sleep(100);
zkClient.createPersistent("/LAN/Node/n3");
sleep(100);
zkClient.delete("/LAN/Node/n1");
sleep(100);
zkClient.delete("/LAN/Node/n2");
sleep(100);
zkClient.delete("/LAN/Node/n3");
sleep(3000);
System.out.println("****");
zkClient.deleteRecursive("/LAN/Node");
sleep(3000);
zkClient.close();
}
private static void sleep(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代码不用过多解释,先创建监听器对/LAN/Node节点进行监听,然后创建节点/LAN/Node及n1,n2,n3三个节点,然后依次删除。当监听器收到事件通知时,打印当前监听节点的所有子节点。
运行结果如下:
看运行结果是不是很漂亮,和代码顺序完全一致,但是否可靠呢?
重要的事情说三遍:是否可靠呢? 是否可靠呢? 是否可靠呢?
稍微修改代码,把部分sleep去掉,如下:
zkClient.createPersistent("/LAN/Node");
// sleep(100);
zkClient.createPersistent("/LAN/Node/n1");
// sleep(100);
zkClient.createPersistent("/LAN/Node/n2");
// sleep(100);
zkClient.createPersistent("/LAN/Node/n3");
// sleep(100);
zkClient.delete("/LAN/Node/n1");
// sleep(100);
zkClient.delete("/LAN/Node/n2");
// sleep(100);
zkClient.delete("/LAN/Node/n3");
// sleep(3000);
System.out.println("****");
zkClient.deleteRecursive("/LAN/Node");
sleep(3000);
zkClient.close();
结果又是那么的不一样:
看起来像是部分事件丢失了。不论是什么原因,总之我们不能坚定地认为这个监听是完全可靠的,不过值得信任的是监听器确实收到了通知。
结语(思考):
本文演示了java向zookeeper注册两种监听事件。1、监听节点数据变化; 2、监听子节点变化。代码功能倒不难理解,不过从演示结果来看,这个监听事件并不能完全捕捉到所有的事件变化。
既然这个监听并不是完全可靠,那是不是这个功能就不能使用呢?其实不然。首先我们可以看到,监听器确实是收到通知了。其次,细心地观察和测试,我们还会发现频繁修改的最后一个事件几乎都能被监听到。
那么我们可以有2个考虑:
1、我们是不是可以考虑确保监听节点上的数据只会变化1次;
2、我们是不是可以考虑不必关心节点频繁变化的中间过程,而是只关心变化后的最终结果;
假如我们根据上面2条,思考设计功能的逻辑,那是不是可以写出可靠的功能呢?当然了,还有更多的方法都可以写出可靠的功能,根据具体问题具体去设计逻辑。
最后,说了这么多,这些功能有什么用呢?这里列举几个
1、监听服务器在线情况。当服务器启动时,往zookeeper中创建一个临时节点,通过监听这些节点,可以知道服务器在线情况;
2、操作完成时通知。当某一个复杂的操作开始时,往zookeeper创建一个节点,操作完成时,把这个节点删掉。监听方可收到操作通知;
3、分布式队列。每来一个操作,先在zookeeper上创建节点,监听之前存在的节点,之间的节点被删完后相当于排队轮到了自己,自己操作完后删除掉自己的节点。
4、分布式锁。和分布式队列相似。
等等......根据需要可以用zookeeper实现一些特殊并且简单巧妙的功能。
The end.
author:蓝何忠
email:lanhezhong@163.com
更多推荐
所有评论(0)