Zookeeper(三):常用JAVA API

Zookeeper Java API简介

  • Zookeeper提供了Java API方便我们来操作zk服务。

  • 通过org.apache.zookeeper.Zookeeper类创建连接zk服务器的示例对象

    • 在创建过程中给定zk服务器地址、会话持续时间以及监视器三个参数

  • 当连接创建成功后,通过Zookeeper实例提供的接口来和服务器进行交互。

Zookeeper类

  • org.apache.zookeeper.Zookeeper类表示连接,该类有几个构造方法,不过一般最常用的是下面两个构造方法:

    • ZooKeeper(connectString,session-Timeout,watcher)

      • 其实这个也是调下面的方法,只是canBeRead-Only是false

    • ZooKeeper(connectString,sessionTimeout,watcher,canBeRead-Only);

      • connectString参数为zk集群服务器的连接url,当给定路径的时候,表示所有的操作都是基于该路径进行操作的(路径只可以添加到最后)。例如: “hh:2181,hh:2182,hh:2183/app”;

      • sessionTimeout为会话过期时间,一般设置为tickTime的3-4倍;

      • watcher是监视器,用于触发相应事件,可以为空;

      • canBeReadOnly是给定是否是只读连接,默认为false。

示例

1.导入依赖

        <!--注入zookeeper-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.6.0</version>
        </dependency>

 

2.连接和创建节点

package com.rzp.service;
​
import org.apache.zookeeper.*;
​
import java.io.IOException;
​
public class DemoConnection {
    public static void main(String[] args) throws Exception {
        //连接地址,写了多个以后,连接时会随机选择一个去连接
        String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183";
        //创建连接,注意sessionTimeout(第二个参数)不能设置太小。。不然一下子就关闭了,后面全都执行不下去。。
        //第三个参数是Watcher类的对象,后文再说明
        ZooKeeper client = new ZooKeeper(url, 48000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {}
        });
        System.out.println("getSessionId=="+client.getSessionId());
​
        //在根目录下创建连接
        /* 参数说明
        1.path 节点,要创建的节点类型
        2.data 节点数据,该节点存储的数据
        3.acl 权限控制,一般使用下面这个就可以了,没有权限控制
        4.CreateMode 节点类型,使用CreateMode下的枚举类就可以了
        返回值就是创建后的节点名称
         */
        String result = client.create("/root", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(result);
​
​
        //在root文件夹下面创建永久顺序子节点child
        result = client.create("/root/child","child".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL);
        System.out.println("添加child节点="+result);
​
        //创建临时节点
        result = client.create("/tmp","tmp".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
        System.out.println("添加tmp节点="+result);
​
        //创建临时顺序节点
        result = client.create("/tmp","tmp".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("添加tmp顺序节点="+result);
​
        Thread.sleep(5000);
        //关闭连接
        client.close();
    }
}
​

 

测试结果:

  • 可以看到节点创建成功了(笔者先创建过/root了,如果第一次走会有4个输出)。

 

 

备注

url后面可以直接增加文件夹,相当于直接连接到app节点,所有操作会在app下操作。

        String url = "192.168.98.129:2181/app";

 

3.删除节点

  
  @Test
    public void test1() throws Exception{
        //创建连接
        String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183";
        ZooKeeper client = new ZooKeeper(url, 48000, null);
​
        //删除节点
        /*
        path:要删除的节点
        version:要删除节点的版本号,如果无视版本号删除就写-1
         */
        client.delete("/root/child0000000000",-1);
        //关闭连接
        client.close();
    }
​

 

查看删除是否成功

[zk: localhost:2181(CONNECTED) 1] ls /root
[child0000000000]
[zk: localhost:2181(CONNECTED) 2] ls /root
[]
​

 

4.获取节点信息

   @Test
    public void testGetSet() throws Exception{
        //创建连接
        String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183";
        ZooKeeper client = new ZooKeeper(url, 48000, null);
//        client.create("/root/child","child".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
        /*参数说明
        path:要查看的节点信息
        watch:true会使用创建连接给的watch监控,false则不用,也可以直接给一个new watch
        stat:输入一个stat类的对象,stat类是节点的状态JavaBean,获取完成以后会把节点信息封装到这个stat里面
         */
        Stat stat = new Stat();
        byte[] data = client.getData("/root/child", false, stat);
        System.out.println(new String(data));
        System.out.println(stat.getCzxid());
        //关闭连接
        client.close();
    }

 

 

 

 

 

 

5.修改节点信息

    @Test
    public void testSet() throws Exception{
        //创建连接
        String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183";
        ZooKeeper client = new ZooKeeper(url, 48000, null);
        /*参数说明
        path:要修改的节点
        data:填入节点的数据
        version:版本,和delete机制一样
         */
        Stat stat = client.setData("/root/child", "new-data".getBytes(), -1);
        System.out.println(stat.getCzxid());
​
        byte[] data = client.getData("/root/child", false, stat);
        System.out.println(new String(data));
        System.out.println(stat.getCzxid());
        //关闭连接
        client.close();
    }

 

 

 

6.其他方法

addAuthInfo: 添加权限验证scheme信息

exists: 判断节点是否存在

getACL: 获取节点的权利列表

setACL: 设置节点的权限列表

getChildren: 获取子节点名称列表

getState: 获取当前连接的States信息

Stat类

  • stat类就是节点的状态JavaBean,可以封装节点的信息。

 

 

Watch

  • Watch机制和shell中的类似,就是开启一个对某节点的监视器以后,这个节点如果有特定的行为,zk会返回给客户端一条信息。

    • 监视器在调用exists、getData、getChildren方法时可以开启。

    • 开启以后一旦被触发,就会执行我们重写的process方法。

    • process方法默认会给一个输入参数WatchedEvent,这里面封装了zk返回来的信息

    @Test
    public void testWatch() throws Exception{
        //创建连接
        String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183";
        ZooKeeper client = new ZooKeeper(url, 48000, new Watcher() {
            //监视器可以在创建连接的时候定义。
            //重写process方法,我们可以让监视器被触发时做一些事情
            @Override
            public void process(WatchedEvent event) {
                //WatchedEvent--zk返回的信息就是封装在这个对象里面,我们可以通过这个对象的方法获取到信息
                //event.getType()--事件类型,见后文
                //event.getPath()--事件发生结点
                System.out.println("创建连接的时候给定的监视器:" + event.getType().name() + ";" + event.getPath());
            }
        });
        //在调用exists、getData、getChildren方法,可以开启创建链接时定义的监视器。
        client.exists("/watcher",true);
        //也可以定义一个新的监视器
        client.exists("/watcher", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("exists自定义监视器"+event.getType().name()+","+event.getPath());
            }
        });
        client.exists("/root",true);
        client.exists("/root/child",true);
        client.getChildren("/root",true);
        Thread.sleep(600000);
        //关闭连接
        client.close();
    }

 


  • event.getType()--事件有这些类型:

    • 比如:使用exists方法开启的监视器,会在被监视的结点创建时触发,返回来的时间类型是NodeCreated。

 

 

  • 同一个结点,监视器开启后,zk服务器仅会发送一次通知信息给客户端。

  • 上图中的child只是第一代子节点----watch机制只监控node节点本身以及node节点的第一代子节点

测试

回到上面给的示例程序,启动以后因为线程休眠,在线程休眠的过程中,我们在shell窗口执行:

[zk: localhost:2181(CONNECTED) 22] create /root/child ""
Created /root/child
[zk: localhost:2181(CONNECTED) 23] delete /root/child
[zk: localhost:2181(CONNECTED) 24] delete /root
​

 

可以看到输出为:

 

 

第一个输出是链接到zk时的输出。

第二条和第三条是/root/child的创建时的反馈,执行了下面代码中的2、3(前面测试代码中摘取出来的)

这时候我们删除child,就没有输出了,验证了同一个结点,开启监视器后只会反馈一次。

最后一条输出时下面代码中的1。

  1      client.exists("/root",true);
  2      client.exists("/root/child",true);
  3      client.getChildren("/root",true);

 

案例

  • 递归监控路径下的所有结点

package com.rzp.service;
​
import java.io.IOException;
import java.util.List;
​
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
​
​
import java.io.IOException;
​
public class DemoWatcher implements Watcher{
    private ZooKeeper client = null;
    private String rootPath = "/app";
​
    public DemoWatcher() throws IOException {
        this.client = new ZooKeeper("192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183", 48000, new Watcher() {
​
            @Override
            public void process(WatchedEvent event) {
                System.out.println("默认监视器:" + event.getType().name());
            }
        });
    }
​
​
    public void run() {
        registerWatcher(this.rootPath, this, true); // 添加监视器
while(true) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
​
    /**
     * 给path路径添加监控信息,循环添加
     *
     * @param path
     * @param watcher
     * @param recursive
     */
    private void registerWatcher(String path, Watcher watcher, boolean recursive) {
        try {
            this.client.exists(path, watcher);
            this.client.getData(path, watcher, null);
            List<String> child = this.client.getChildren(path, watcher);
            //递归监控
            if (recursive && child != null) {
                for (String childPath : child) {
                    registerWatcher(path + "/" + childPath, watcher, recursive);
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
​
    @Override
    public void process(WatchedEvent event) {
        try {
            switch (event.getType()) {
                case NodeChildrenChanged:
                    System.out.println(event.getPath() + "节点子节点列表发生改变");
                    this.registerWatcher(event.getPath(), this, true); // 重新添加一遍监视器
                    break;
                case NodeCreated:
                    System.out.println(event.getPath() + "节点被创建");
                    this.registerWatcher(event.getPath(), this, false);
                    break;
                case NodeDataChanged:
                    System.out.println(event.getPath() + "节点内容被修改");
                    this.registerWatcher(event.getPath(), this, false);
                    break;
                case NodeDeleted:
                    System.out.println(event.getPath() + "节点被删除");
                    this.client.exists(event.getPath(), this); // 添加监视器
                    break;
                case None:
                    System.out.println("触发none event");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    public static void main(String[] args) throws IOException {
        new DemoWatcher().run();
    }
}

 


 

posted @ 2020-04-21 23:51  renzhongpei  阅读(697)  评论(0编辑  收藏  举报