ZooKeeper Watch Java API浅析getChildren

Posted lipeng_bigdata

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZooKeeper Watch Java API浅析getChildren相关的知识,希望对你有一定的参考价值。

        Watch是ZooKeeper中非常重要的一个机制,它可以监控ZooKeeper中节点的变化情况,告知客户端。下面,我们以代码为例来分析Watch在ZooKeeper中是如何实现的。ZooKeeper中一共由三种方法可以实现Watch,分别为getData、exists和getChildren,今天我们先来看下getChildren()方法:

        3、getChildren

import java.io.IOException;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

public class TestZooKeeperWatcher {

	public static void main(String[] args) {

		ZooKeeper zk = null;
		try {

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			System.out.println("开始连接ZooKeeper...");

			// 创建与ZooKeeper服务器的连接zk
			String address = "192.168.1.226:2181";
			int sessionTimeout = 3000;
			zk = new ZooKeeper(address, sessionTimeout, new Watcher() {
				// 监控所有被触发的事件
				public void process(WatchedEvent event) {
					if (event.getType() == null || "".equals(event.getType())) {
						return;
					}
					System.out.println("已经触发了" + event.getType() + "事件!");
				}
			});

			System.out.println("ZooKeeper连接创建成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 创建根目录节点
			// 路径为/tmp_root_path
			// 节点内容为字符串"我是根目录/tmp_root_path"
			// 创建模式为CreateMode.PERSISTENT
			System.out.println("开始创建根目录节点/tmp_root_path...");
			zk.create("/tmp_root_path", "我是根目录/tmp_root_path".getBytes(),
					Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
			System.out.println("根目录节点/tmp_root_path创建成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			
			// 获取子目录节点列表
			System.out.println("开始获取根目录/tmp_root_path节点的子目录节点列...");
			System.out.println(zk.getChildren("/tmp_root_path", true));
			System.out.println("根目录/tmp_root_path节点的子目录节点列获取成功!");
			
			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 创建第一个子目录节点
			// 路径为/tmp_root_path/childPath1
			// 节点内容为字符串"我是第一个子目录/tmp_root_path/childPath1"
			// 创建模式为CreateMode.PERSISTENT
			System.out.println("开始创建第一个子目录节点/tmp_root_path/childPath1...");
			zk.create("/tmp_root_path/childPath1",
					"我是第一个子目录/tmp_root_path/childPath1".getBytes(),
					Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
			System.out.println("第一个子目录节点/tmp_root_path/childPath1创建成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 创建第二个子目录节点
			// 路径为/tmp_root_path/childPath2
			// 节点内容为字符串"我是第二个子目录/tmp_root_path/childPath2"
			// 创建模式为CreateMode.PERSISTENT
			System.out.println("开始创建第二个子目录节点/tmp_root_path/childPath2...");
			zk.create("/tmp_root_path/childPath2",
					"我是第二个子目录/tmp_root_path/childPath2".getBytes(),
					Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
			System.out.println("第二个子目录节点/tmp_root_path/childPath2创建成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 修改第一个子目录节点/tmp_root_path/childPath1数据
			System.out.println("开始修改第一个子目录节点/tmp_root_path/childPath1数据...");
			zk.setData("/tmp_root_path/childPath1",
					"我是修改数据后的第一个子目录/tmp_root_path/childPath1".getBytes(), -1);
			System.out.println("修改第一个子目录节点/tmp_root_path/childPath1数据成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 修改第二个子目录节点/tmp_root_path/childPath2数据
			System.out.println("开始修改第二个子目录节点/tmp_root_path/childPath2数据...");
			zk.setData("/tmp_root_path/childPath2",
					"我是修改数据后的第二个子目录/tmp_root_path/childPath2".getBytes(), -1);
			System.out.println("修改第二个子目录节点/tmp_root_path/childPath2数据成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			
			// 修改根目录节点数据
			System.out.println("开始修改根目录节点/tmp_root_path数据...");
			zk.setData("/tmp_root_path",
					"我是修改数据后的根目录/tmp_root_path".getBytes(), -1);
			System.out.println("修改根目录节点/tmp_root_path数据成功!");
			
			Thread.currentThread().sleep(1000l);
			
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			
			// 删除第一个子目录节点
			System.out.println("开始删除第一个子目录节点/tmp_root_path/childPath1...");
			zk.delete("/tmp_root_path/childPath1", -1);
			System.out.println("第一个子目录节点/tmp_root_path/childPath1删除成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 删除第二个子目录节点
			System.out.println("开始删除第二个子目录节点/tmp_root_path/childPath2...");
			zk.delete("/tmp_root_path/childPath2", -1);
			System.out.println("第二个子目录节点/tmp_root_path/childPath2删除成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 删除根目录节点
			System.out.println("开始删除根目录节点/tmp_root_path...");
			zk.delete("/tmp_root_path", -1);
			System.out.println("根目录节点/tmp_root_path删除成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

		} catch (IOException | KeeperException | InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			// 关闭连接
			if (zk != null) {
				try {
					zk.close();
					System.out.println("释放ZooKeeper连接成功!");

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}
}
        执行结果如下:

...
...
...
...
开始连接ZooKeeper...
ZooKeeper连接创建成功!
已经触发了None事件!
...
...
...
...
开始创建根目录节点/tmp_root_path...
根目录节点/tmp_root_path创建成功!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始创建第一个子目录节点/tmp_root_path/childPath1...
第一个子目录节点/tmp_root_path/childPath1创建成功!
已经触发了NodeChildrenChanged事件!
...
...
...
...
开始创建第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2创建成功!
...
...
...
...
开始修改第一个子目录节点/tmp_root_path/childPath1数据...
修改第一个子目录节点/tmp_root_path/childPath1数据成功!
...
...
...
...
开始修改第二个子目录节点/tmp_root_path/childPath2数据...
修改第二个子目录节点/tmp_root_path/childPath2数据成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始删除第一个子目录节点/tmp_root_path/childPath1...
第一个子目录节点/tmp_root_path/childPath1删除成功!
...
...
...
...
开始删除第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2删除成功!
...
...
...
...
开始删除根目录节点/tmp_root_path...
根目录节点/tmp_root_path删除成功!
...
...
...
...
释放ZooKeeper连接成功!
        而当我们在获取子目录节点列表getChildren()方法调用之后,接着调用修改根目录节点数据的setData()方法,添加代码和结果如下:

			// 修改根目录节点数据
			System.out.println("开始修改根目录节点/tmp_root_path数据...");
			zk.setData("/tmp_root_path",
					"我是修改数据后的根目录/tmp_root_path".getBytes(), -1);
			System.out.println("修改根目录节点/tmp_root_path数据成功!");
			
			Thread.currentThread().sleep(1000l);
			
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
...
...
...
...
开始连接ZooKeeper...
ZooKeeper连接创建成功!
已经触发了None事件!
...
...
...
...
开始创建根目录节点/tmp_root_path...
根目录节点/tmp_root_path创建成功!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始创建第一个子目录节点/tmp_root_path/childPath1...
已经触发了NodeChildrenChanged事件!
第一个子目录节点/tmp_root_path/childPath1创建成功!
...
...
...
...
开始创建第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2创建成功!
...
...
...
...
开始修改第一个子目录节点/tmp_root_path/childPath1数据...
修改第一个子目录节点/tmp_root_path/childPath1数据成功!
...
...
...
...
开始修改第二个子目录节点/tmp_root_path/childPath2数据...
修改第二个子目录节点/tmp_root_path/childPath2数据成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始删除第一个子目录节点/tmp_root_path/childPath1...
第一个子目录节点/tmp_root_path/childPath1删除成功!
...
...
...
...
开始删除第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2删除成功!
...
...
...
...
开始删除根目录节点/tmp_root_path...
根目录节点/tmp_root_path删除成功!
...
...
...
...
释放ZooKeeper连接成功!
        它仍然只是监控根目录下的子节点变化情况,而且触发的是NodeChildrenChanged事件!而当我们在创建第一个子节点后如果再创建它的一个子节点,并且在创建之前还是先获取根目录/tmp_root_path节点的子目录节点列,执行结果会怎么样呢?添加的代码和执行结果如下:

			// 获取子目录节点列表
			System.out.println("开始获取根目录/tmp_root_path节点的子目录节点列...");
			System.out.println(zk.getChildren("/tmp_root_path", true));
			System.out.println("根目录/tmp_root_path节点的子目录节点列获取成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");

			// 创建第一个子目录节点的子节点
			// 路径为/tmp_root_path/childPath1/childPath1
			// 节点内容为字符串"我是第一个子目录/tmp_root_path/childPath1/childPath1"
			// 创建模式为CreateMode.PERSISTENT
			System.out
					.println("开始创建第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1...");
			zk.create("/tmp_root_path/childPath1/childPath1",
					"我是第一个子目录的子节点/tmp_root_path/childPath1/childPath1"
							.getBytes(), Ids.OPEN_ACL_UNSAFE,
					CreateMode.PERSISTENT);
			System.out
					.println("第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1创建成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
...
...
...
...
开始连接ZooKeeper...
ZooKeeper连接创建成功!
已经触发了None事件!
...
...
...
...
开始创建根目录节点/tmp_root_path...
根目录节点/tmp_root_path创建成功!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始创建第一个子目录节点/tmp_root_path/childPath1...
第一个子目录节点/tmp_root_path/childPath1创建成功!
已经触发了NodeChildrenChanged事件!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[childPath1]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始创建第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1...
第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1创建成功!
...
...
...
...
开始创建第二个子目录节点/tmp_root_path/childPath2...
已经触发了NodeChildrenChanged事件!
第二个子目录节点/tmp_root_path/childPath2创建成功!
...
...
...
...
开始修改第一个子目录节点/tmp_root_path/childPath1数据...
修改第一个子目录节点/tmp_root_path/childPath1数据成功!
...
...
...
...
开始修改第二个子目录节点/tmp_root_path/childPath2数据...
修改第二个子目录节点/tmp_root_path/childPath2数据成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始删除第一个子目录节点/tmp_root_path/childPath1...
org.apache.zookeeper.KeeperException$NotEmptyException: KeeperErrorCode = Directory not empty for /tmp_root_path/childPath1
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:125)
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)
	at org.apache.zookeeper.ZooKeeper.delete(ZooKeeper.java:873)
	at com.jngreen.bgm.scheduler.TestZooKeeperWatcher.main(TestZooKeeperWatcher.java:197)
释放ZooKeeper连接成功!
        还是只会监控直接子目录下的节点,在增加第二个节点时触发NodeChildrenChanged事件,并不会越级监控!当然,出现org.apache.zookeeper.KeeperException$NotEmptyException异常是因为我们删除第一个节点时,由于其还有节点,所以才会报错!

        还有一件有意思的事情,当我们在修改第一个子节点数据前获取根目录/tmp_root_path节点的子目录节点列表,调用getChildren()方法,结果会怎样呢?添加处的代码和执行结果如下:

			// 获取子目录节点列表
			System.out.println("开始获取根目录/tmp_root_path节点的子目录节点列...");
			System.out.println(zk.getChildren("/tmp_root_path", true));
			System.out.println("根目录/tmp_root_path节点的子目录节点列获取成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			
			// 修改第一个子目录节点/tmp_root_path/childPath1数据
			System.out.println("开始修改第一个子目录节点/tmp_root_path/childPath1数据...");
			zk.setData("/tmp_root_path/childPath1",
					"我是修改数据后的第一个子目录/tmp_root_path/childPath1".getBytes(), -1);
			System.out.println("修改第一个子目录节点/tmp_root_path/childPath1数据成功!");

			Thread.currentThread().sleep(1000l);

			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
			System.out.println("...");
...
...
...
...
开始连接ZooKeeper...
ZooKeeper连接创建成功!
已经触发了None事件!
...
...
...
...
开始创建根目录节点/tmp_root_path...
根目录节点/tmp_root_path创建成功!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始创建第一个子目录节点/tmp_root_path/childPath1...
已经触发了NodeChildrenChanged事件!
第一个子目录节点/tmp_root_path/childPath1创建成功!
...
...
...
...
开始创建第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2创建成功!
...
...
...
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[childPath2, childPath1]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始修改第一个子目录节点/tmp_root_path/childPath1数据...
修改第一个子目录节点/tmp_root_path/childPath1数据成功!
...
...
...
...
开始修改第二个子目录节点/tmp_root_path/childPath2数据...
修改第二个子目录节点/tmp_root_path/childPath2数据成功!
...
...
...
...
开始修改根目录节点/tmp_root_path数据...
修改根目录节点/tmp_root_path数据成功!
...
...
...
...
开始删除第一个子目录节点/tmp_root_path/childPath1...
已经触发了NodeChildrenChanged事件!
第一个子目录节点/tmp_root_path/childPath1删除成功!
...
...
...
...
开始删除第二个子目录节点/tmp_root_path/childPath2...
第二个子目录节点/tmp_root_path/childPath2删除成功!
...
...
...
...
开始删除根目录节点/tmp_root_path...
根目录节点/tmp_root_path删除成功!
...
...
...
...
释放ZooKeeper连接成功!
        也是只监控一次,但是,但是,但是,它只监控根目录子节点的增减情况,至于数据是否发生变化,完全不会监控!这也正是为什么修改第一个子节点数据没有触发,而删除第一个子节点时会触发NodeChildrenChanged事件的原因!


        结论:

         getChildren()方法仅仅监控对应节点直接子目录的一次变化,但是只会监控直接子节点的增减情况,不会监控数据变化情况!若要每次对应节点发生增减变化都被监测到,那么每次都得先调用getChildren()方法获取一遍节点的子节点列表!










以上是关于ZooKeeper Watch Java API浅析getChildren的主要内容,如果未能解决你的问题,请参考以下文章

06_zookeeper原生Java API使用

zookeeper学习-5Java API操作 - Watcher监听机制

Zookeeper -- Zookeeper JavaAPI相关操作(Curator介绍Curator API 常用操作(节点的CRUD,Watch事件监听)分布式锁模拟12306售票案例)

Apache Curator操作zookeeper的API使用

ZooKeeper如何做到统一配置

原理系列之——zookeeper的watch监控机制