【3. Zookeeper的Java 客户端框架】

Java 客户端框架

Zookeeper 原生客户端

<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>enjoy</groupId>
    <artifactId>zookeeperJavaApi</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.12</version>
        </dependency>
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

创建会话


public class TestCreateSession {
    // zookeeper服务器地址
    private static final String SERVER = "192.168.71.128:2181";

    // 回话超时
    private final int SESSION_TIMEOUT = 30000;
    //发令枪
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 获得 session 的方式,这种方式可能会在 ZooKeeper 还没有获得连接的时候就已经对 ZK 进行访问了
     */
    @Test
    public void testSession1() throws Exception {
        ZooKeeper zooKeeper = new ZooKeeper(SERVER, SESSION_TIMEOUT, null);
        System.out.println(zooKeeper);
        System.out.println(zooKeeper.getState()); // CONNECTING
    }

    /**
     * 对获得 Session 的方式进行优化,在 ZooKeeper 初始化完成以前先等待,等待完成后再进行后续操作
     */
    @Test
    public void testSession2() throws Exception {
        ZooKeeper zooKeeper = new ZooKeeper(SERVER, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    //确认已经连接完毕后再进行操作
                    countDownLatch.countDown();
                    System.out.println("已经获得了连接");
                }
            }
        });

        //连接完成之前先等待
        countDownLatch.await(); // 等待连接成功的发令枪
        System.out.println(zooKeeper.getState()); // CONNECTED
    }
}

客户端基本操作

public class TestJavaApi implements Watcher {

    private static final int SESSION_TIMEOUT = 10000;
    private static final String CONNECTION_STRING = "192.168.71.128:2181";
    private static final String ZK_PATH = "/leader";
    private ZooKeeper zooKeeper = null;

    private CountDownLatch connectedSemaphore = new CountDownLatch(1);

    public static void main(String[] args) {
        TestJavaApi sample = new TestJavaApi();
        sample.createConnection(CONNECTION_STRING, SESSION_TIMEOUT); // 创建session
        if (sample.createPath(ZK_PATH, "我是节点初始内容")) { // 创建节点
            System.out.println();
            System.out.println("数据内容: " + sample.readData(ZK_PATH) + "\n"); // 读取数据
            sample.writeData(ZK_PATH, "更新后的数据"); // 更新数据
            System.out.println("数据内容: " + sample.readData(ZK_PATH) + "\n");
            sample.deleteNode(ZK_PATH); // 删除数据
        }

        sample.releaseConnection(); // 释放session连接
    }

    public void createConnection(String connectString, int sessionTimeout) {
        this.releaseConnection();
        try {
            zooKeeper = new ZooKeeper(connectString, sessionTimeout, this); // 创建zookeeper
            connectedSemaphore.await(); // 等待发令信号: session创建成功
        } catch (InterruptedException e) {
            System.out.println("连接创建失败,发生 InterruptedException"); // 被中断
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("连接创建失败,发生 IOException"); // 可能是超时
            e.printStackTrace();
        }
    }

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

    public boolean createPath(String path, String data) {
        try {
            String actualPath = this.zooKeeper.create(
                path, // 节点路径: the actual path of the created node
                data.getBytes(), // 节点内容
                Ids.OPEN_ACL_UNSAFE, // 节点权限
                CreateMode.EPHEMERAL // 节点类型: PERSISTENT PERSISTENT_SEQUENTIAL EPHEMERAL EPHEMERAL_SEQUENTIAL
            );
            System.out.println("节点创建成功, Path: " + actualPath + ", content: " + data);
        } catch (KeeperException e) {
            System.out.println("节点创建失败,发生 KeeperException");
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("节点创建失败,发生 InterruptedException");
            e.printStackTrace();
        }
        return true;
    }

    public String readData(String path) {
        try {
            System.out.println("获取数据成功,path:" + path);
            return new String(this.zooKeeper.getData(path, false, null));
        } catch (KeeperException e) {
            System.out.println("读取数据失败,发生 KeeperException,path: " + path);
            e.printStackTrace();
            return "";
        } catch (InterruptedException e) {
            System.out.println("读取数据失败,发生 InterruptedException,path: " + path);
            e.printStackTrace();
            return "";
        }
    }

    public boolean writeData(String path, String data) { // 更新指定节点数据内容
        try {
            System.out.println("更新数据成功,path:" + path + ", stat: " +
                this.zooKeeper.setData(path, data.getBytes(), -1) // 更新
            );
        } catch (KeeperException e) {
            System.out.println("更新数据失败,发生 KeeperException,path: " + path);
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("更新数据失败,发生 InterruptedException,path: " + path);
            e.printStackTrace();
        }
        return false;
    }

    public void deleteNode(String path) { // 删除指定节点
        try {
            this.zooKeeper.delete(path, -1);
            System.out.println("删除节点成功,path:" + path);
        } catch (KeeperException e) {
            System.out.println("删除节点失败,发生 KeeperException,path: " + path);
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("删除节点失败,发生 InterruptedException,path: " + path);
            e.printStackTrace();
        }
    }

    @Override
    public void process(WatchedEvent event) { // 收到来自 Server 的 Watcher 通知后的处理。
        System.out.println("收到事件通知:" + event.getState() + "\n");
        if (Event.KeeperState.SyncConnected == event.getState()) {
            connectedSemaphore.countDown();
        }

    }
}

Watch机制

public class ZooKeeperWatcher implements Watcher {
    private static final int SESSION_TIMEOUT = 10000; // 定义 session 失效时间
    private static final String CONNECTION_ADDR = "192.168.71.128:2181";
    private static final String PARENT_PATH = "/testWatch"; // 父路径设置
    private static final String CHILDREN_PATH = "/testWatch/children";
    private static final String LOG_PREFIX_OF_MAIN = "【Main】"; // 进入标识
    AtomicInteger seq = new AtomicInteger(); // 定义原子变量
    private ZooKeeper zk = null; // zk 变量
    // 信号量设置,用于等待 zookeeper 连接建立之后 通知阻塞程序继续向下执行
    private final CountDownLatch connectedSemaphore = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {
        //建立 watcher
        ZooKeeperWatcher zkWatch = new ZooKeeperWatcher();
        //创建连接
        zkWatch.createConnection(CONNECTION_ADDR, SESSION_TIMEOUT);
        System.out.println(zkWatch.zk.toString());

        Thread.sleep(1000);
        // 清理节点
        zkWatch.deleteAllTestPath();
        if (zkWatch.createPath(PARENT_PATH, System.currentTimeMillis() + "")) { // 创建节点
            // 读取数据,在操作节点数据之前先调用 zookeeper 的 getData()方法是为了可以 watch 到对节点的操作。【watch 是一次性的】,
            // 也就是说,如果第二次又重新调用了 setData()方法,在此之前需要重新调
            System.out.println("---------------------- read parent ----------------------------");
            zkWatch.readData(PARENT_PATH, true);
            // 更新数据
            zkWatch.writeData(PARENT_PATH, System.currentTimeMillis() + "");

            // 读取子节点,设置对子节点变化的 watch,如果不写该方法,则在创建子节点是只会输出 NodeCreated,而不会输出 NodeChildrenChanged,
            // 也就是说创建子节点时没有 watch。
            // 如果是递归的创建子节点,如 path="/p/c1/c2"的话,getChildren(PARENT_PATH,
            // ture)只会在创建 c1 时 watch,输出 c1 的 NodeChildrenChanged,
            // 而 不 会 输 出 创 建 c2 时 的 NodeChildrenChanged , 如 果 watch 到 c2 的
            // NodeChildrenChanged,则需要再调用一次 getChildren(String path, true)方法, 其中 path="/p/c1"
            System.out.println("---------------------- read children path ----------------------------");
            zkWatch.getChildren(PARENT_PATH, true);

            Thread.sleep(1000);
            // 创建子节点,同理如果想要 watch 到 NodeChildrenChanged 状态,需要调用getChildren(CHILDREN_PATH, true)
            zkWatch.createPath(CHILDREN_PATH, System.currentTimeMillis() + "");

            Thread.sleep(1000);

            zkWatch.readData(CHILDREN_PATH, true);
            zkWatch.writeData(CHILDREN_PATH, System.currentTimeMillis() + "");
        }

        Thread.sleep(50000);
        // 清理节点
        zkWatch.deleteAllTestPath();
        Thread.sleep(1000);
        zkWatch.releaseConnection();
    }

    public void createConnection(String connectAddr, int sessionTimeout) {
        this.releaseConnection();
        try {
            zk = new ZooKeeper(connectAddr, sessionTimeout, this); // 创建zookeeper
            System.out.println(LOG_PREFIX_OF_MAIN + "开始连接 ZK 服务器");
            connectedSemaphore.await(); // 等待session创建成功
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    public boolean createPath(String path, String data) { // 创建节点
        try {
            // 设置监控(由于 zookeeper 的监控都是一次性的所以每次必须设置监控)
            this.zk.exists(path, true);
            System.out.println(LOG_PREFIX_OF_MAIN + "节点创建成功, Path: " + this.zk.create(
                path, // 路径
                data.getBytes(), // 数 据
                ZooDefs.Ids.OPEN_ACL_UNSAFE, // 所 有 可 见
                CreateMode.PERSISTENT) + // 永 久 存 储
                ", content: " + data);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public String readData(String path, boolean needWatch) { // 读取指定节点数据内容
        try {
            return new String(this.zk.getData(path, needWatch, null)); // 读取并重新设置监听
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public boolean writeData(String path, String data) { // 更新指定节点数据内容
        try {
            // 更新数据不需要重新设置监听
            System.out.println("更新数据成功,path:" + path + ", stat: " + this.zk.setData(path, data.getBytes(), -1));
        } catch (Exception e) {
            e.printStackTrace();

        }
        return false;
    }

    public void deleteNode(String path) { // 删除指定节点
        try {
            this.zk.delete(path, -1);
            System.out.println(LOG_PREFIX_OF_MAIN + "删除节点成功,path:" + path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Stat exists(String path, boolean needWatch) { // 判断指定节点是否存在
        try {
            return this.zk.exists(path, needWatch);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private List<String> getChildren(String path, boolean needWatch) { // 获取子节点
        try {
            return this.zk.getChildren(path, needWatch);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public void deleteAllTestPath() { // 删除所有节点
        if (this.exists(CHILDREN_PATH, false) != null) {
            this.deleteNode(CHILDREN_PATH);
        } // 先删除子,再删除父
        if (this.exists(PARENT_PATH, false) != null) {
            this.deleteNode(PARENT_PATH);
        }
    }

    @Override // 一个钩子方法
    public void process(WatchedEvent event) { // 收到来自 Server 的 Watcher 通知后的处理。
        System.out.println("进入 process 。。。。。event = " + event);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (event == null) {
            return;
        }
        Watcher.Event.KeeperState keeperState = event.getState(); // 连接状态
        Watcher.Event.EventType eventType = event.getType(); // 事件类型
        String path = event.getPath(); // 受影响的 path

        String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";

        System.out.println(logPrefix + " 收 到 Watcher 通 知 ");
        System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
        System.out.println(logPrefix + "事件类型:\t" + eventType.toString());

        if (Event.KeeperState.SyncConnected == keeperState) {
            if (Event.EventType.None == eventType) { // 成功连接上 ZK 服务器
                System.out.println(logPrefix + "成功连接上 ZK 服务器");
                connectedSemaphore.countDown();
            } else if (Event.EventType.NodeCreated == eventType) { //创建节点
                System.out.println(logPrefix + "节点创建");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.exists(path, true);
            } else if (Event.EventType.NodeDataChanged == eventType) { //更新节点
                System.out.println(logPrefix + "节点数据更新");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(logPrefix + " 数据内容: " + this.readData(PARENT_PATH, true));
            } else if (Event.EventType.NodeChildrenChanged == eventType) { //更新子节点
                System.out.println(logPrefix + "子节点变更");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(logPrefix + "	子	节	点	列	表	:	" + this.getChildren(PARENT_PATH, true));
            } else if (Event.EventType.NodeDeleted == eventType) { //删除节点
                System.out.println(logPrefix + "节点 " + path + " 被删除");
            }
        } else if (Watcher.Event.KeeperState.Disconnected == keeperState) {
            System.out.println(logPrefix + "与 ZK 服务器断开连接");
        } else if (Watcher.Event.KeeperState.AuthFailed == keeperState) {
            System.out.println(logPrefix + "权限检查失败");
        } else if (Watcher.Event.KeeperState.Expired == keeperState) {
            System.out.println(logPrefix + "会话失效");
        }

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

ZK 认证机制

public class TestZookeeperAuth implements Watcher {
    final static String CONNECT_ADDR = "192.168.71.128:2181"; // 连接地址
    final static String PATH = "/testAuth"; // 测试路径
    final static String PATH_DEL = "/testAuth/delNode";
    final static String authentication_type = "digest"; // 认证类型
    final static String correctAuthentication = "123456"; // 认证正确方法
    final static String badAuthentication = "654321"; // 认证错误方法
    private static final String LOG_PREFIX_OF_MAIN = "【Main】"; // 标识
    static ZooKeeper zk = null;
    private final CountDownLatch connectedSemaphore = new CountDownLatch(1); // 设置为1,只需要等待一个线程
    AtomicInteger seq = new AtomicInteger(); // 计时器

    public static void main(String[] args) throws Exception {

        TestZookeeperAuth testAuth = new TestZookeeperAuth();
        testAuth.createConnection(CONNECT_ADDR, 2000);

        List<ACL> acls = new ArrayList<>(1);
        acls.addAll(Ids.CREATOR_ALL_ACL);

        try {
            zk.create(PATH, "init content".getBytes(), acls, CreateMode.PERSISTENT);
            System.out.println("使用授权 key:" + correctAuthentication + "创建节点:" + PATH
                + ", 初始内容是: init content");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            zk.create(PATH_DEL, "will be deleted! ".getBytes(), acls, CreateMode.PERSISTENT);
            System.out.println("使用授权 key:" + correctAuthentication + "创建节点:" + PATH_DEL + ", 初始内容是: init content");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 获取数据
        getDataByNoAuthentication();
        getDataByBadAuthentication();
        getDataByCorrectAuthentication();
        // 更新数据
        updateDataByNoAuthentication();
        updateDataByBadAuthentication();
        updateDataByCorrectAuthentication();
        // 删除数据
        deleteNodeByBadAuthentication();
        deleteNodeByNoAuthentication();
        deleteNodeByCorrectAuthentication();
        Thread.sleep(1000);

        deleteParent();
        //释放连接
        testAuth.releaseConnection();
    }

    static void getDataByBadAuthentication() { // 获取数据:采用错误的密码
        String prefix = "[使用错误的授权信息]";
        try {
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授权
            badzk.addAuthInfo(authentication_type, badAuthentication.getBytes());
            Thread.sleep(2000);
            System.out.println(prefix + "获取数据:" + PATH);
            System.out.println(prefix + "成功获取数据:" + Arrays.toString(badzk.getData(PATH, false, null)));
        } catch (Exception e) {
            System.err.println(prefix + "获取数据失败,原因:" + e.getMessage());
        }
    }

    static void getDataByNoAuthentication() { // 获取数据:不采用密码, 没有addAuthInfo()
        String prefix = "[不使用任何授权信息]";
        try {
            System.out.println(prefix + "获取数据:" + PATH);
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            System.out.println(prefix + "成功获取数据:" + Arrays.toString(nozk.getData(PATH, false, null)));
        } catch (Exception e) {
            System.err.println(prefix + "获取数据失败,原因:" + e.getMessage());
        }
    }

    static void getDataByCorrectAuthentication() { // 采用正确的密码
        String prefix = "[使用正确的授权信息]";
        try {
            System.out.println(prefix + "获取数据:" + PATH);

            System.out.println(prefix + "成功获取数据:" + Arrays.toString(zk.getData(PATH, false, null)));
        } catch (Exception e) {
            System.out.println(prefix + "获取数据失败,原因:" + e.getMessage());
        }
    }

    static void updateDataByNoAuthentication() { // 更新数据:不采用密码
        String prefix = "[不使用任何授权信息]";
        System.out.println(prefix + "更新数据: " + PATH);
        try {
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            Stat stat = nozk.exists(PATH, false);
            if (stat != null) {
                nozk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
        }
    }

    static void updateDataByBadAuthentication() { // 更新数据:采用错误的密码

        String prefix = "[使用错误的授权信息]";

        System.out.println(prefix + "更新数据:" + PATH);
        try {
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授权
            badzk.addAuthInfo(authentication_type, badAuthentication.getBytes());
            Thread.sleep(2000);
            Stat stat = badzk.exists(PATH, false);
            if (stat != null) {
                badzk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
        }
    }

    static void updateDataByCorrectAuthentication() { // 更新数据:采用正确的密码
        String prefix = "[使用正确的授权信息]";
        System.out.println(prefix + "更新数据:" + PATH);
        try {
            Stat stat = zk.exists(PATH, false);
            if (stat != null) {
                zk.setData(PATH, prefix.getBytes(), -1);
                System.out.println(prefix + "更新成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
        }
    }

    static void deleteNodeByNoAuthentication() { // 不使用密码 删除节点
        String prefix = "[不使用任何授权信息]";
        try {
            System.out.println(prefix + " 删 除 节 点 :" + PATH_DEL);
            ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            Thread.sleep(2000);
            Stat stat = nozk.exists(PATH_DEL, false);
            if (stat != null) {
                nozk.delete(PATH_DEL, -1);
                System.out.println(prefix + "删除成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "删除失败,原因是:" + e.getMessage());
        }
    }

    static void deleteNodeByBadAuthentication() { // 采用错误的密码删除节点
        String prefix = "[使用错误的授权信息]";
        try {
            System.out.println(prefix + " 删 除 节 点 :" + PATH_DEL);
            ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
            //授权
            badzk.addAuthInfo(authentication_type, badAuthentication.getBytes());
            Thread.sleep(2000);
            Stat stat = badzk.exists(PATH_DEL, false);
            if (stat != null) {
                badzk.delete(PATH_DEL, -1);
                System.out.println(prefix + "删除成功");
            }
        } catch (Exception e) {
            System.err.println(prefix + "删除失败,原因是:" + e.getMessage());
        }
    }

    static void deleteNodeByCorrectAuthentication() { // 使用正确的密码删除节点
        String prefix = "[使用正确的授权信息]";
        try {
            System.out.println(prefix + "删除节点:" + PATH_DEL);
            Stat stat = zk.exists(PATH_DEL, false);
            if (stat != null) {
                zk.delete(PATH_DEL, -1);
                System.out.println(prefix + "删除成功");
            }
        } catch (Exception e) {
            System.out.println(prefix + "删除失败,原因是:" + e.getMessage());
        }
    }

    static void deleteParent() { // 使用正确的密码删除节点
        try {
            Stat stat = zk.exists(PATH_DEL, false);
            if (stat == null) {
                zk.delete(PATH, -1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void process(WatchedEvent event) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (event == null) {
            return;
        }

        Event.KeeperState keeperState = event.getState(); // 连接状态
        Event.EventType eventType = event.getType(); // 事件类型
        String path = event.getPath(); // 受影响的 path

        String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";

        System.out.println(logPrefix + " 受影响的 path: " + path);
        System.out.println(logPrefix + " 收 到 Watcher 通 知 ");
        System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
        System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
        if (Event.KeeperState.SyncConnected == keeperState) {
            if (Event.EventType.None == eventType) { // 成功连接上 ZK 服务器
                System.out.println(logPrefix + "成功连接上 ZK 服务器");
                connectedSemaphore.countDown();// 发送连接成功信号
            }
        } else if (Event.KeeperState.Disconnected == keeperState) {
            System.out.println(logPrefix + "与 ZK 服务器断开连接");
        } else if (Event.KeeperState.AuthFailed == keeperState) {
            System.out.println(logPrefix + "权限检查失败");
        } else if (Event.KeeperState.Expired == keeperState) {
            System.out.println(logPrefix + "会话失效");
        }
        System.out.println("--------------------------------------------");
    }

    public void createConnection(String connectString, int sessionTimeout) { // 创建 ZK 连接
        this.releaseConnection();
        try {
            zk = new ZooKeeper(connectString, sessionTimeout, this);
            //添加节点授权
            zk.addAuthInfo(authentication_type, correctAuthentication.getBytes()); // digest
            System.out.println(LOG_PREFIX_OF_MAIN + "开始连接 ZK 服务器");
            connectedSemaphore.await(); //倒数等待
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

zkClient

基本操作

public class ZkClientOperator {
    static final String CONNECT_ADDR = "192.168.71.128:2181"; // zookeeper 地址
    static final int SESSION_OUTTIME = 10000; // session 超时时间 ms

    public static void main(String[] args) throws Exception {
        // ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
        ZkClient zkc = new ZkClient(CONNECT_ADDR, SESSION_OUTTIME);
        //1. create and delete 方法
        zkc.createEphemeral("/temp");
        zkc.createPersistent("/super/c1", true);
        Thread.sleep(10000);
        zkc.delete("/temp");
        zkc.deleteRecursive("/super");
        //2. 设置 path 和 data 并且读取子节点和每个节点的内容
        zkc.createPersistent("/super", "1234");
        zkc.createPersistent("/super/c1", "c1 内 容 ");
        zkc.createPersistent("/super/c2", "c2 内容");
        List<String> list = zkc.getChildren("/super");
        for (String p : list) {
            System.out.println(p);
            String rp = "/super/" + p;
            String data = zkc.readData(rp);
            System.out.println("节点为:" + rp + ",内容为: " + data);
        }
        //3. 更新和判断节点是否存在zkc.writeData("/super/c1", " 新 内 容 ");
        System.out.println(zkc.readData("/super/c1").toString());
        System.out.println(zkc.exists("/super/c1"));
        //	4.递归删除/super 内容
        zkc.deleteRecursive("/super");
    }
}

发现代码比原生方法少了好多

监听机制

public class TestZkClientWatcher {
    /**
     * zookeeper 地址
     */
    static final String CONNECT_ADDR = "192.168.71.128:2181";
    /**
     * session 超时时间
     */
    static final int SESSION_OUTTIME = 10000;//ms

    @Test
    public void testZkClientWatcher1() throws Exception { // subscribeChildChanges 方法 订阅子节点变化
        ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
        //对父节点添加监听子节点变化。
        zkc.subscribeChildChanges("/super", new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                System.out.println("parentPath: " + parentPath);
                System.out.println("currentChilds: " + currentChilds);
            }
        });

        Thread.sleep(3000);
        zkc.createPersistent("/super");
        Thread.sleep(1000);

        zkc.createPersistent("/super" + "/" + "c1", "c1 内容");
        Thread.sleep(1000);

        zkc.createPersistent("/super" + "/" + "c2", "c2 内容");
        Thread.sleep(1000);

        zkc.delete("/super/c2");
        Thread.sleep(1000);

        zkc.deleteRecursive("/super");
        Thread.sleep(Integer.MAX_VALUE);

    }

    @Test
    public void testZkClientWatcher2() throws Exception { // subscribeDataChanges 订阅内容变化
        ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
        zkc.createPersistent("/super", "1234");
        //对父节点添加监听子节点变化。
        zkc.subscribeDataChanges("/super", new IZkDataListener() {
            @Override
            public void handleDataDeleted(String path) throws Exception {
                System.out.println("删除的节点为:" + path);
            }
            @Override
            public void handleDataChange(String path, Object data) throws Exception {
                System.out.println("变更的节点为:" + path + ", 变更内容为:" + data);
            }
        });

        Thread.sleep(3000);
        zkc.writeData("/super", "456", -1);
        Thread.sleep(1000);
        zkc.delete("/super");
        Thread.sleep(Integer.MAX_VALUE);
    }
}

Curator

基本操作

添加事务依赖

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.0.0</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-test</artifactId>
    <version>2.12.0</version>
</dependency>

<!-- 注意需要注释掉原生的zookeeper依赖 -->
<!--    <dependency>-->
<!--      <groupId>org.apache.zookeeper</groupId>-->
<!--      <artifactId>zookeeper</artifactId>-->
<!--      <version>3.4.6</version>-->
<!--    </dependency>-->
public class OperatorTest {
    //ZooKeeper 服务地址
    private static final String SERVER = "192.168.71.128:2181";
    //会话超时时间
    private final int SESSION_TIMEOUT = 30000;
    //连接超时时间
    private final int CONNECTION_TIMEOUT = 5000;
    /*
     * baseSleepTimeMs:初始的重试等待时间 maxRetries:最多重试次数
     * ExponentialBackoffRetry:重试一定次数,每次重试时间依次递增 RetryNTimes:
     * 重试 N 次 RetryOneTime:重试一次 RetryUntilElapsed:重试一定时间
     */
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    //创建连接实例
    private CuratorFramework curatorFramework = null;

    @org.junit.Before
    public void init() {
        //创建 CuratorFrameworkImpl 实例
        curatorFramework = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, retryPolicy);
        //启动
        curatorFramework.start();
    }

    @Test
    public void testCreate() throws Exception { // 测试创建节点
        //创建永久节点
        curatorFramework.create().forPath("/curator", "/curator data".getBytes());
        //创建永久有序节点
        curatorFramework.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL)
            .forPath("/curator_sequential", "/curator_sequential data".getBytes());
        //创建临时节点
        curatorFramework.create().withMode(CreateMode.EPHEMERAL)
            .forPath("/curator/ephemeral", "/curator/ephemeral data".getBytes());
        //创建临时有序节点
        curatorFramework.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
            .forPath("/curator/ephemeral_path1", "/curator/ephemeral_path1 data".getBytes());
    }

    @Test
    public void testCheck() throws Exception { // 测试检查某个节点是否存在
        Stat stat1 = curatorFramework.checkExists().forPath("/curator");
        Stat stat2 = curatorFramework.checkExists().forPath("/curator2");

        System.out.println("'/curator'是否存在: " + (stat1 != null));
        System.out.println("'/curator2'是否存在: " + (stat2 != null));
    }

    @Test
    public void testSetDataAsync() throws Exception { // 测试异步设置节点数据
        //创建监听器
        CuratorListener listener = (client, event) -> System.out.println(event.getPath());
        //添加监听器
        curatorFramework.getCuratorListenable().addListener(listener);
        //异步设置某个节点数据
        curatorFramework.setData().inBackground().forPath("/curator", "sync".getBytes());
        //为了防止单元测试结束从而看不到异步执行结果,因此暂停 10 秒
        Thread.sleep(10000);
    }

    @Test
    public void testSetDataAsyncWithCallback() throws Exception { // 测试另一种异步执行获取通知的方式
        BackgroundCallback callback = (client, event) -> System.out.println(event.getPath());
        //异步设置某个节点数据
        curatorFramework.setData().inBackground(callback).forPath("/curator", "/curator modified data with Callback ".getBytes());
        //为了防止单元测试结束从而看不到异步执行结果,因此暂停 10 秒
        Thread.sleep(10000);
    }

    @Test
    public void testDelete() throws Exception { // 测试删除节点
        //创建测试节点
        curatorFramework.create().orSetData().creatingParentsIfNeeded()
            .forPath("/curator/del_key1", "/curator/del_key1 data".getBytes());
        curatorFramework.create().orSetData().creatingParentsIfNeeded()
            .forPath("/curator/del_key2", "/curator/del_key2 data".getBytes());
        curatorFramework.create().forPath("/curator/del_key2/test_key", "test_key data".getBytes());
        //删除该节点
        curatorFramework.delete().forPath("/curator/del_key1");
        //级联删除子节点
        curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath("/curator/del_key2");
    }

    @Test
    public void testTransaction() throws Exception { // 测试事务管理:碰到异常,事务会回滚
        //定义几个基本操作
        CuratorOp createOp = curatorFramework.transactionOp().create()
            .forPath("/curator/one_path", "some data".getBytes());
        CuratorOp setDataOp = curatorFramework.transactionOp().setData()
            .forPath("/curator", "other data".getBytes());
        CuratorOp deleteOp = curatorFramework.transactionOp().delete()
            .forPath("/curator");
        //事务执行结果
        List<CuratorTransactionResult> results = curatorFramework.transaction()
            .forOperations(createOp, setDataOp, deleteOp);
        //遍历输出结果
        for (CuratorTransactionResult result : results) {
            System.out.println("执行结果是: " + result.getForPath() + "--" + result.getType());
        }
    }
}

监听机制

public class EventTest {
    //ZooKeeper 服务地址
    private static final String SERVER = "192.168.71.128:2181";
    //会话超时时间
    private final int SESSION_TIMEOUT = 30000;
    //连接超时时间
    private final int CONNECTION_TIMEOUT = 5000;
    /*
     *	baseSleepTimeMs:初始的重试等待时间
     *	maxRetries:最多重试次数
     *	ExponentialBackoffRetry:重试一定次数,每次重试时间依次递增
     *	RetryNTimes:重试 N 次
     *	RetryOneTime:重试一次
     *	RetryUntilElapsed:重试一定时间
     */
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    //创建连接实例
    private CuratorFramework client = null;

    @org.junit.Before
    public void init() {
        //创建 CuratorFrameworkImpl 实例
        client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, retryPolicy);
        //启动
        client.start();
    }

    @Test
    public void TestListenterOne() throws Exception { // 对指定的节点进行添加操作 仅仅能监控指定的本节点的数据修改,删除 操作 并且只能监听一次 --->不好
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());
        // 注册观察者,当节点变动时触发
        byte[] data = client.getData().usingWatcher(new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("获取 test 节点 监听器 : " + event);
            }
        }).forPath("/test");
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());
        Thread.sleep(1000);
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());
        Thread.sleep(1000);
        System.out.println("节点数据: " + new String(data));
        Thread.sleep(10000);
    }

    /**
     * @描述:第二种监听器的添加方式: Cache 的三种实现 Path Cache:监视一个路径下 1)孩子结点的创建、2)删除,3)以及结点数据的更新。 产生的事件会传递给注册的 PathChildrenCacheListener。 Node
     * Cache:监视一个结点的创建、更新、删除,并将结点的数据缓存在本地。 Tree Cache:Path Cache 和 Node Cache 的“合体”,监视路径下的创建、更新、删除事件,并缓存路径下所有孩子结点的数据。
     */
    //1.path Cache	连接	路径	是否获取数据
    //能监听所有的字节点 且是无限监听的模式 但是 指定目录下节点的子节点不再监听
    @Test
    public void setListenterTwoOne() throws Exception {
        ExecutorService pool = Executors.newCachedThreadPool();
        PathChildrenCache childrenCache = new PathChildrenCache(client, "/test", true);
        PathChildrenCacheListener childrenCacheListener = (client, event) -> {
            System.out.println("开始进行事件分析:-----");
            ChildData data = event.getData();
            switch (event.getType()) {
                case CHILD_ADDED:
                    System.out.println("CHILD_ADDED : " + data.getPath() + "	数据:" + Arrays.toString(data.getData()));

                    System.out.println("开始进行事件分析:-----");

                    break;
                case CHILD_REMOVED:
                    System.out.println("CHILD_REMOVED  :  " + data.getPath() + "	数据:" + Arrays.toString(data.getData()));
                    break;
                case CHILD_UPDATED:
                    System.out.println("CHILD_UPDATED  :  " + data.getPath() + "  数据:" + Arrays.toString(data.getData()));
                    break;
                case INITIALIZED:
                    System.out.println("CHILD_INITIALIZED  :  " + data.getPath() + "  数据:" + Arrays.toString(data.getData()));
                    break;
                default:
                    break;
            }
        };
        childrenCache.getListenable().addListener(childrenCacheListener);
        System.out.println("Register zk watcher successfully!");
        childrenCache.start(StartMode.POST_INITIALIZED_EVENT);
        //创建一个节点
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());

        client.create().orSetData().withMode(CreateMode.EPHEMERAL).forPath("/test/node01", "enjoy".getBytes());
        Thread.sleep(1000);

        client.create().orSetData().withMode(CreateMode.EPHEMERAL).forPath("/test/node02", "deer".getBytes());
        Thread.sleep(1000);

        client.create().orSetData().withMode(CreateMode.EPHEMERAL).forPath("/test/node02", "demo".getBytes());
        Thread.sleep(1000);
        client.delete().forPath("/test/node02");
        Thread.sleep(10000);
    }

    //2.Node Cache	监控本节点的变化情况	连接 目录 是否压缩
    //监听本节点的变化	节点可以进行修改操作	删除节点后会再次创建(空节点)
    @Test
    public void setListenterTwoTwo() throws Exception {
        ExecutorService pool = Executors.newCachedThreadPool();
        //设置节点的 cache
        final NodeCache nodeCache = new NodeCache(client, "/test", false);
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("the test node is change and result is :");
                System.out.println("path : " + nodeCache.getCurrentData().getPath());
                System.out.println("data	:	" + new
                    String(nodeCache.getCurrentData().getData()));
                System.out.println("stat : " + nodeCache.getCurrentData().getStat());
            }
        });
        nodeCache.start();
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());
        Thread.sleep(1000);

        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "enjoy".getBytes()
        );
        Thread.sleep(10000);
    }

    //3.Tree Cache
    //  监控 指定节点和节点下的所有的节点的变化--无限监听	可以进行本节点的删除(不在创建)
    @Test
    public void TestListenterTwoThree() throws Exception {
        ExecutorService pool = Executors.newCachedThreadPool();
        //设置节点的 cache
        TreeCache treeCache = new TreeCache(client, "/test");
        //设置监听器和处理过程
        treeCache.getListenable().addListener((client, event) -> {
            ChildData data = event.getData();
            if (data != null) {
                switch (event.getType()) {
                    case NODE_ADDED:
                        System.out.println("NODE_ADDED  :  " + data.getPath() + "	数据:" + new String(data.getData()));
                        break;
                    case NODE_REMOVED:
                        System.out.println("NODE_REMOVED : " + data.getPath() + "数据:" + new String(data.getData()));
                        break;
                    case NODE_UPDATED:
                        System.out.println("NODE_UPDATED : " + data.getPath() + "数据:" + new String(data.getData()));
                        break;
                    default:
                        break;
                }
            } else {
                System.out.println("data is null : " + event.getType());
            }
        });
        //开始监听
        treeCache.start();
        //创建一个节点
        client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath("/test", "test".getBytes());
        Thread.sleep(1000);

        client.create().orSetData().withMode(CreateMode.EPHEMERAL).forPath("/test/node01", "enjoy".getBytes());
        Thread.sleep(1000);

        client.create().orSetData().withMode(CreateMode.EPHEMERAL).forPath("/test/node01", "deer".getBytes());

        Thread.sleep(1000);

        client.create().orSetData().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
            .forPath("/test/node02/node02_2", "deer".getBytes());
        Thread.sleep(10000);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岁月玲珑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值