zookeeper功能介绍(三)---java在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

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值