ZooKeeper之Watcher机制

1.watcher原理框架

 

   zk的watcher由客户端,客户端WatchManager,zk服务器组成。整个过程涉及了消息通信及数据存储。

  • zk客户端向zk服务器注册watcher的同时,会将watcher对象存储在客户端的watchManager。
  • Zk服务器触发watcher事件后,会向客户端发送通知,客户端线程从watchManager中回调watcher执行相应的功能。

  EventType完整的类名是org.apache.zookeeper.Watcher.Event.EventType

  

  • NodeDataChanged事件

  无论节点数据发生变化还是数据版本发生变化都会触发

  即使被更新数据与新数据一样,数据版本dataVersion都会发生变化

  • NodeChildrenChanged

  新增节点或者删除节点

  • AuthFailed

  重点是客户端会话没有权限而是授权失败

watcher注册过程

  创建zk客户端对象实例时注册:

ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean 
canBeReadOnly)
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd)
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly)

  通过这种方式注册的watcher将会作为整个zk会话期间的默认watcher,会一直被保存在客户端ZK WatchManager 的 defaultWatcher 中,如果这个被创建的节点在其它时候被创建watcher并注册,则这个默认的watcher会被覆盖。注意注意注意,watcher触发一次就会失效,不管是创建节点时的 watcher 还是以后创建的 watcher。

其他注册watcher的API:

  • getChildren(String path, Watcher watcher)
  • getChildren(String path, boolean watch)
  • Boolean watch表示是否使用上下文中默认的watcher,即创建zk实例时设置的watcher
  • getData(String path, boolean watch, Stat stat)
  • Boolean watch表示是否使用上下文默认的watcher,即创建zk实例时设置的watcher
  • getData(String path, Watcher watcher, AsyncCallback.DataCallback cb, Object ctx)
  • exists(String path, boolean watch)
  • Boolean watch表示是否使用上下文中默认的watcher,即创建zk实例时设置的watcher
  • exists(String path, Watcher watcher)

  wather的特性:一次性,客户端串行执行,轻量。

  一次性:对于ZK的watcher,只需要记住一点:zookeeper有watch事件,是一次性触发的,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher,由于zookeeper的监控都是一次性的,所以每次都必须监控。

  客户端串行执行:客户端watcher回调的过程是一个串行同步的过程,这为我们保证了顺序,同时需要开发人员注意一点,千万不用因为一个watcher的处理逻辑影响了整个客户端的watcher回调。

       轻量:WatcherEvent是Zookeeper整个Watcher通知机制的最小通知单元。整个单元结构只包含三部分:通知状态,事件类型和节点路径。也就是说Watcher通知非常的简单,只会告诉客户端发生了事件而不会告知其具体内容,需要客户自己去进行获取,而不会直接提供具体的数据内容。

package com.smart.zookeeper;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class ZooKeeperWatcher implements Watcher {

    private AtomicInteger seq = new AtomicInteger();

    private static final int SESSION_TIMEOUT = 10000;

    private static final String CONNECT_ADDRESS = "192.168.223.142:2181";
    /**
     * 父路径
     */
    private static final String ROOT_PATH = "/root";
    /**
     * 子路径
     */
    private static final String CHILD_PATH = "/root/child";

    private ZooKeeper zk = null;
    /**
     * 用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行
     */
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 创建连接
     *
     * @param connectAddr    ZK服务器地址列表
     * @param sessionTimeout Session超时时间
     */
    public void createConnection(String connectAddr, int sessionTimeout) {
        releaseConnection();
        try {
            //this表示把当前对象进行传递到其中去(也就是在主函数里实例化的new ZooKeeperWatcher()实例对象)
            zk = new ZooKeeper(connectAddr, sessionTimeout, this);
            System.out.println("开始连接ZK服务器");
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭ZK连接
     */
    public void releaseConnection() {
        try {
            if (zk != null) {
                zk.close();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建节点
     *
     * @param path
     * @param data
     * @param isWatch
     * @return
     */
    public boolean createPath(String path, String data, boolean isWatch) {
        try {
            Stat stat = zk.exists(path, isWatch);
            if (stat == null) {
                String nodePath = zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                System.out.println("创建节点成功 path:" + nodePath);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 读取指定节点的内容
     *
     * @param path
     * @param isWatch
     * @return
     */
    public String readData(String path, boolean isWatch) {
        try {
            return new String(zk.getData(path, isWatch, null));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 更新指定的节点的数据
     *
     * @param path
     * @param data
     * @return
     */
    public boolean writeData(String path, String data) {
        try {
            zk.setData(path, data.getBytes(), -1);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除指定的节点
     *
     * @param path
     */
    public void deleteNode(String path) {
        try {
            zk.delete(path, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除所有节点
     */
    public void deleteAllNode(boolean isWatcher) throws KeeperException, InterruptedException {
        if (zk.exists(CHILD_PATH, isWatcher) != null) {
            deleteNode(CHILD_PATH);
        }

        if (zk.exists(ROOT_PATH, isWatcher) != null) {
            deleteNode(ROOT_PATH);
        }
    }


    @Override
    public void process(WatchedEvent watchedEvent) {
        System.out.println("event  :" + watchedEvent);
        if (watchedEvent == null) return;
        //连接状态
        Event.KeeperState state = watchedEvent.getState();
        //事件类型
        Event.EventType eventType = watchedEvent.getType();
        //受影响path
        String path = watchedEvent.getPath();
        System.out.println("连接状态:\t" + state.toString());
        System.out.println("事件类型:\t" + eventType.toString());

        if (Event.KeeperState.SyncConnected == state) {
            // 成功连接上ZK服务器
            if (Event.EventType.None == eventType) {
                System.out.println("成功连接上ZK服务器");
                countDownLatch.countDown();
            } else if (Event.EventType.NodeCreated == eventType) {
                System.out.println("节点创建");
            } else if (Event.EventType.NodeDataChanged == eventType) {
                System.out.println("节点数据更新");
            } else if (Event.EventType.NodeChildrenChanged == eventType) {
                System.out.println("子节点变更");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else if (Event.EventType.NodeDeleted == eventType) {
                System.out.println("节点 " + path + " 被删除");
            }
        } else if (Event.KeeperState.Disconnected == state) {
            System.out.println("与ZK服务器断开连接");
        } else if (Event.KeeperState.AuthFailed == state) {
            System.out.println("权限检查失败");
        } else if (Event.KeeperState.Expired == state) {
            System.out.println("会话失效");
        }
    }

    public static void main(String[] args) throws InterruptedException, KeeperException {
        ZooKeeperWatcher zkWatcher = new ZooKeeperWatcher();
        zkWatcher.createConnection(CONNECT_ADDRESS, SESSION_TIMEOUT);
        System.out.println("--------"+zkWatcher.zk.toString());

        if(zkWatcher.createPath(ROOT_PATH, System.currentTimeMillis() + "", false)) {
            Thread.sleep(1000);
            // 读取数据
            zkWatcher.readData(ROOT_PATH, false);
            // 更新数据
            zkWatcher.writeData(ROOT_PATH, System.currentTimeMillis() + "");
            Thread.sleep(1000);

            // 创建子节点
            zkWatcher.createPath(CHILD_PATH, System.currentTimeMillis() + "", false);

            //在进行修改之前,我们需要watch一下这个节点:
            Thread.sleep(1000);
            zkWatcher.readData(CHILD_PATH, true);
            zkWatcher.writeData(CHILD_PATH, System.currentTimeMillis() + "");
        }

        zkWatcher.deleteAllNode(false);
        zkWatcher.releaseConnection();
    }
}

 

posted on 2019-10-08 23:08  溪水静幽  阅读(630)  评论(0)    收藏  举报