原文:http://blog.csdn.net/lipeng_bigdata/article/details/50985811
Watch是ZooKeeper中非常重要的一个机制,它可以监控ZooKeeper中节点的变化情况,告知客户端。下面,我们以代码为例来分析Watch在ZooKeeper中是如何实现的。ZooKeeper中一共由三种方法可以实现Watch,分别为getData、exists和getChildren,今天我们先来看下getData()方法:
1、getData()
- 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;
- 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("...");
- // 创建第一个子目录节点
- // 路径为/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("...");
- 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/childPath2节点数据
- System.out.println("开始获取第二个子目录节点/tmp_root_path/childPath2节点数据...");
- System.out.println(new String(zk.getData(
- "/tmp_root_path/childPath2", true, null)));
- 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/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/childPath1...
- 第一个子目录节点/tmp_root_path/childPath1创建成功!
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- ...
- 开始创建第二个子目录节点/tmp_root_path/childPath2...
- 第二个子目录节点/tmp_root_path/childPath2创建成功!
- ...
- ...
- ...
- ...
- 开始获取第二个子目录节点/tmp_root_path/childPath2节点数据...
- 我是第二个子目录/tmp_root_path/childPath2
- 第二个子目录节点/tmp_root_path/childPath2节点数据获取成功!
- ...
- ...
- ...
- ...
- 开始修改第一个子目录节点/tmp_root_path/childPath1数据...
- 修改第一个子目录节点/tmp_root_path/childPath1数据成功!
- ...
- ...
- ...
- ...
- 开始修改第二个子目录节点/tmp_root_path/childPath2数据...
- 已经触发了NodeDataChanged事件!
- 修改第二个子目录节点/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删除成功!
- ...
- ...
- ...
- ...
- 释放ZooKeeper连接成功!
而当我们屏蔽到修改第二个子节点/tmp_root_path/childPath2数据相关代码时,屏蔽掉的部分和执行结果如下:
- // // 修改第二个子目录节点/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("...");
- ...
- ...
- ...
- ...
- 开始连接ZooKeeper...
- ZooKeeper连接创建成功!
- 已经触发了None事件!
- ...
- ...
- ...
- ...
- 开始创建根目录节点/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/childPath2节点数据...
- 我是第二个子目录/tmp_root_path/childPath2
- 第二个子目录节点/tmp_root_path/childPath2节点数据获取成功!
- ...
- ...
- ...
- ...
- 开始修改第一个子目录节点/tmp_root_path/childPath1数据...
- 修改第一个子目录节点/tmp_root_path/childPath1数据成功!
- ...
- ...
- ...
- ...
- 开始删除第一个子目录节点/tmp_root_path/childPath1...
- 第一个子目录节点/tmp_root_path/childPath1删除成功!
- ...
- ...
- ...
- ...
- 开始删除第二个子目录节点/tmp_root_path/childPath2...
- 已经触发了NodeDeleted事件!
- 第二个子目录节点/tmp_root_path/childPath2删除成功!
- ...
- ...
- ...
- ...
- 开始删除根目录节点/tmp_root_path...
- 根目录节点/tmp_root_path删除成功!
- ...
- ...
- ...
- ...
- 释放ZooKeeper连接成功!
我们再做一个变更,修改第二个子节点/tmp_root_path/childPath2两次,那么执行结果如何呢?添加的代码及执行结果如下:
- // 第二次修改第二个子目录节点/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("...");
- ...
- ...
- ...
- ...
- 开始连接ZooKeeper...
- ZooKeeper连接创建成功!
- 已经触发了None事件!
- ...
- ...
- ...
- ...
- 开始创建根目录节点/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/childPath2节点数据...
- 我是第二个子目录/tmp_root_path/childPath2
- 第二个子目录节点/tmp_root_path/childPath2节点数据获取成功!
- ...
- ...
- ...
- ...
- 开始修改第一个子目录节点/tmp_root_path/childPath1数据...
- 修改第一个子目录节点/tmp_root_path/childPath1数据成功!
- ...
- ...
- ...
- ...
- 开始修改第二个子目录节点/tmp_root_path/childPath2数据...
- 已经触发了NodeDataChanged事件!
- 修改第二个子目录节点/tmp_root_path/childPath2数据成功!
- ...
- ...
- ...
- ...
- 开始第二次修改第二个子目录节点/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删除成功!
- ...
- ...
- ...
- ...
- 释放ZooKeeper连接成功!
而当我们在第二次修改第二个子节点/tmp_root_path/childPath2数据前先获取一遍,并且watch设置为true,那么两次对第二个子节点/tmp_root_path/childPath2数据的修改均会触发NodeDataChanged事件,并且获取根目录节点数据时,也仅是监控根目录,其子目录的变化不会触发NodeDataChanged事件,读者可自行尝试!
结论:
getData()方法仅仅监控对应节点的一次数据变化,无论是数据修改还是删除!若要每次对应节点发生变化都被监测到,那么每次都得先调用getData()方法获取一遍数据!