watcher

watcher架构:

  Watcher实现由三个部分组成:

    Zookeeper服务端

    Zookeeper客户端

    客户端的ZKWatchManager对象

  客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当Zookeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调响应的处理逻辑,从而完成整体的数据发布/订阅流程。

  

watcher特性:

  

watcher接口设计:

  Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。Watcher内部包含了两个枚类:KeeperState、EventType

    

   

  

 捕获相应的事件:

  建立zookeeper的watcher监听:在zookeeper中采用zk.getChildren(path,watch)、zk.exists(path,watch)、zk.getData(path,watcher,stat)这样的方式为某个znode注册监听。

  下表以node-x节点为例,说明调用的注册方法和可监听事件间的关系:

    

 注册watcher的方法:

  1.客户端与服务器的连接状态

    

public class ZKConnectionWatcher implements Watcher {
    // 计数器对象
    static CountDownLatch countDownLatch = new CountDownLatch(1);
    // 连接对象
    static ZooKeeper zooKeeper;

    @Override
    public void process(WatchedEvent event) {
        try {
            // 事件类型
            if (event.getType() == Event.EventType.None) {
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    System.out.println("连接创建成功");
                    countDownLatch.countDown(); // 通知线程可以继续往下执行了
                } else if (event.getState() == Event.KeeperState.Disconnected) {
                    System.out.println("断开连接");
                } else if (event.getState() == Event.KeeperState.Expired) {
                    System.out.println("会话超时");
                    // 会话超时,重新创建
                    zooKeeper = new ZooKeeper("192.168.43.182:2181", 5000, new ZKConnectionWatcher());
                } else if (event.getState() == Event.KeeperState.AuthFailed) {
                    System.out.println("认证失败");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            zooKeeper = new ZooKeeper("192.168.43.182:2181", 5000, new ZKConnectionWatcher());
            countDownLatch.await(); // 阻塞线程,等待连接的创建
            // 会话id
            System.out.println(zooKeeper.getSessionId());

            // 添加授权用户
            zooKeeper.addAuthInfo("digest", "fan:123456".getBytes());
            byte[] bytes = zooKeeper.getData("/node1", false, null);
            System.out.println(new String(bytes));

            Thread.sleep(5000);
            System.out.println("结束");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (zooKeeper != null) {
                try {
                    zooKeeper.close();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
View Code

  2.检查节点是否存在(watcher是一次性的,下面演示创建节点、更新节点、删除节点,每次都需要重新启动测试类,启动一次连续测试的话只有第一次编辑节点可以监听到)

public class ZKWatcherExists {

    private String IP = "192.168.43.182:2181";
    private ZooKeeper zooKeeper;

    @Before
    public void before() {
        try {
            // 计数器对象
            CountDownLatch countDownLatch = new CountDownLatch(1);

            // 参数1:服务器的ip和端口    参数2:客户端和服务器之间的会话超时时间,以毫秒为单位 参数3:监视器对象
            zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        System.out.println("连接创建成功!");
                        countDownLatch.countDown();
                    }
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                }
            });
            // 主线程阻塞等待连接对象的创建成功
            countDownLatch.await();
            // 会话编号
            System.out.println(zooKeeper.getSessionId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @After
    public void after() {
        if (zooKeeper != null) {
            try {
                zooKeeper.close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void watcherExists1 () throws Exception {
        // 参数1:节点的路径 参数2:是否使用连接对象中注册的监视器
        zooKeeper.exists("/watcher1", true);
        Thread.sleep(50000);
        System.out.println("结束");
        /**
         * 启动测试类后,在zookeeper的客户端创建节点(create /watcher1 "aaaa")
         * 可以看到控制台输出:
         *          path=/watcher1
         *          eventType=NodeCreated
         *
         * 同样的方式可以测试节点数据的改变:
         *          path=/watcher1
         *          eventType=NodeDataChanged
         *
         * 节点的删除:
         *          path=/watcher1
         *          eventType=NodeDeleted
         */
    }

    @Test
    public void watcherExists2 () throws Exception {
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());

                    // watcher的创建是一次性的,如果需要连续监听:
                    zooKeeper.exists("/watcher1", this);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        // 自定义watcher对象
        zooKeeper.exists("/watcher1", watcher);
        Thread.sleep(50000);
        System.out.println("结束");
    }

    // 注册多个监听器对象,每个监听器都能监听到
    @Test
    public void watcherExists3 () throws Exception {
        zooKeeper.exists("/watcher1", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("1");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        zooKeeper.exists("/watcher1", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("2");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        Thread.sleep(50000);
        System.out.println("结束");
    }
}
View Code

  3.查看节点

    @Test
    public void watcherGetData1() throws Exception {
        zooKeeper.getData("/watcher2", true, null);
        Thread.sleep(50000);
        System.out.println("结束");
        /**
         * 修改节点数据,可以看到控制台输出:
         *          path=/watcher2
         *          eventType=NodeDataChanged
         *
         * 节点的删除:
         *          path=/watcher2
         *          eventType=NodeDeleted
         */
    }

    @Test
    public void watcherGetData2() throws Exception {
        zooKeeper.getData("/watcher2", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("自定义watcher");
                System.out.println("path=" + event.getPath());
                System.out.println("eventType=" + event.getType());
            }
        }, null);
        Thread.sleep(50000);
        System.out.println("结束");

        /**watcher连续监听(需要判断如果是删除节点,则不需要再继续监听)、注册多个监听器对象的用法与检查节点是否存在的用法一样**/
    }
View Code

  4.查看子节点

    @Test
    public void watcherGetChildren1() throws Exception {
        zooKeeper.getChildren("/watcher3", true);
        Thread.sleep(50000);
        System.out.println("结束");
        /**
         * 创建子节点、删除子节点、递归删除所有节点(rmr watcher3),都可以看到控制台输出:
         *          path=/watcher3
         *          eventType=NodeChildrenChanged
         *
         * 删除节点watcher3:
         *          path=/watcher3
         *          eventType=NodeDeleted
         */
    }

    @Test
    public void watcherGetChildren2() throws Exception {
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                    // 如果是根节点被删除,没必须再继续监听
                    if (event.getType() == Event.EventType.NodeChildrenChanged) {
                        zooKeeper.getChildren("/watcher3", this);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        zooKeeper.getChildren("/watcher3", watcher);
        Thread.sleep(50000);
        System.out.println("结束");

        /**注册多个监听器对象的用法与前面的例子一样**/
    }
View Code

 

posted @ 2020-05-28 18:18  糖不甜,盐不咸  阅读(519)  评论(0编辑  收藏  举报