ZooKeeper Watch Java API浅析getChildren

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

        3、getChildren

import org.apache.zookeeper.*;

import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhubo
 * Date: 2018-01-11
 * Time: 10:27
 */
public class ZooKeeper_Watch_GetChildren {

    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 = "centos4:2181";
            int sessionTimeout = 3000;
            zk = new ZooKeeper(address, sessionTimeout, new Watcher() {
                //监控所有被触发的事件
                @Override
                public void process(WatchedEvent event) {
                    if (event.getType() == null || "".equals(event.getType())) {
                        return;
                    }
                    System.out.println("已经触发了" + event.getType() + "事件!");
                }
            });

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

            Thread.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(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("根目录节点/tmp_root_path创建成功!");

            Thread.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.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(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("第一个子目录节点/tmp_root_path/childPath1创建成功!");

            Thread.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(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("第二个子目录节点/tmp_root_path/childPath2创建成功!");

            Thread.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.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.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.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.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.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.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();
                }
            }
        }

    }

}

执行结果如下:

[2018-01-11 10:50:47.624  INFO org.apache.zookeeper.ZooKeeper:438] [main] Initiating client connection, connectString=centos4:2181 sessionTimeout=3000 watcher=com.timer.watch.ZooKeeper_Watch_GetChildren$1@17d99928
ZooKeeper连接创建成功!
[2018-01-11 10:50:47.748  INFO org.apache.zookeeper.ClientCnxn:1032] [main-SendThread(centos4:2181)] Opening socket connection to server centos4/192.168.20.244:2181. Will not attempt to authenticate using SASL (unknown error)
[2018-01-11 10:50:47.750  INFO org.apache.zookeeper.ClientCnxn:876] [main-SendThread(centos4:2181)] Socket connection established to centos4/192.168.20.244:2181, initiating session
[2018-01-11 10:50:47.767  INFO org.apache.zookeeper.ClientCnxn:1299] [main-SendThread(centos4:2181)] Session establishment complete on server centos4/192.168.20.244:2181, sessionid = 0x160db22da860058, negotiated timeout = 4000
已经触发了None事件!
...
...
...
...
开始创建根目录节点/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删除成功!
...
...
...
...
[2018-01-11 10:50:58.839  INFO org.apache.zookeeper.ZooKeeper:684] [main] Session: 0x160db22da860058 closed
释放ZooKeeper连接成功!

Process finished with exit code 0

而当我们在获取子目录节点列表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.sleep(1000L);

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

 

[2018-01-11 10:58:36.157  INFO org.apache.zookeeper.ZooKeeper:438] [main] Initiating client connection, connectString=centos4:2181 sessionTimeout=3000 watcher=com.timer.watch.ZooKeeper_Watch_GetChildren$1@17d99928
ZooKeeper连接创建成功!
[2018-01-11 10:58:36.322  INFO org.apache.zookeeper.ClientCnxn:1032] [main-SendThread(centos4:2181)] Opening socket connection to server centos4/192.168.20.244:2181. Will not attempt to authenticate using SASL (unknown error)
[2018-01-11 10:58:36.324  INFO org.apache.zookeeper.ClientCnxn:876] [main-SendThread(centos4:2181)] Socket connection established to centos4/192.168.20.244:2181, initiating session
[2018-01-11 10:58:36.336  INFO org.apache.zookeeper.ClientCnxn:1299] [main-SendThread(centos4:2181)] Session establishment complete on server centos4/192.168.20.244:2181, sessionid = 0x160db22da860059, negotiated timeout = 4000
已经触发了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删除成功!
...
...
...
...
[2018-01-11 10:58:48.411  INFO org.apache.zookeeper.ZooKeeper:684] [main] Session: 0x160db22da860059 closed
释放ZooKeeper连接成功!

它仍然只是监控根目录下的子节点变化情况,而且触发的是NodeChildrenChanged事件!

如果我们修改第一个子节点数据之前对父节点添加getChildren监控结果如下

110833_pOBZ_3101476.png

110902_EgM8_3101476.png

可见修改子节点中数据信息并不会触发getChildren监听事件。

而当我们在创建第一个子节点后如果再创建它的一个子节点,并且在创建之前还是先获取根目录/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.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(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.PERSISTENT);
            System.out
                    .println("第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1创建成功!");

            Thread.sleep(1000L);
...
开始获取根目录/tmp_root_path节点的子目录节点列...
[childPath1, childPath2]
根目录/tmp_root_path节点的子目录节点列获取成功!
...
...
...
...
开始创建第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1...
第一个子目录节点的子节点/tmp_root_path/childPath1/childPath1创建成功!
...
...





开始删除第一个子目录节点/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.timer.watch.ZooKeeper_Watch_GetChildren.main(ZooKeeper_Watch_GetChildren.java:231)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
[2018-01-11 11:17:28.222  INFO org.apache.zookeeper.ZooKeeper:684] [main] Session: 0x160db22da86005b closed
释放ZooKeeper连接成功!

还是只会监控直接子目录下的节点,在增加第二个节点时触发NodeChildrenChanged事件,并不会越级监控!当然,出现org.apache.zookeeper.KeeperException$NotEmptyException异常是因为我们删除第一个节点时,由于其还有节点,所以才会报错!

它只监控根目录子节点的增减情况,至于数据是否发生变化,完全不会监控!这也正是为什么修改第一个子节点数据没有触发,而删除第一个子节点时会触发NodeChildrenChanged事件的原因!

结论:

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

 

 

转载于:https://my.oschina.net/LucasZhu/blog/1605544

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值