Zookeeper-------Apache Curator操作API

Zookeeper-------Apache Curator操作API

目录

Zookeeper-------Apache Curator操作API

1、操作前准备工作和原生API的一些缺点

2、Curator的API详解

2.1 创建会话

2.2 创建结点

2.3 删除结点和修改结点

2.4 获取某个结点的信息

2.5 curator使用usingWatcher(与原生API一次监听)

2.6 curator使用nodeCache(一次注册N次监听)

2.7curator使用PathChildrenCache 子节点监听

2.8 curator之acl权限操作


1、操作前准备工作和原生API的一些缺点

    idea创建一个maven项目,并引入以下依赖,并使用centos启动zookeeper服务。

        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
        </dependency>

curator简介与客户端之间的异同点:

(1)ZooKeeper原生Java API的不足之处:

  • 在连接zk超时的时候,不支持自动重连,需要手动操作
  • Watch注册一次就会失效,需要反复注册
  • 不支持递归创建节点

(2)Apache curator:

  • Apache 的开源项目
  • 解决Watch注册一次就会失效的问题
  • 提供的 API 更加简单易用
  • 提供更多解决方案并且实现简单,例如:分布式锁
  • 提供常用的ZooKeeper工具类
  • 编程风格更舒服,

2、Curator的API详解

2.1 创建会话

 CuratorFramework cc = CuratorFrameworkFactory.builder()  //使用工厂类来建造客户端的实例对象
                .connectString("ip:port")      //服务器地址
                .sessionTimeoutMs(2000)        //会话过期时间
                .connectionTimeoutMs(5000)     //重连时间
                .retryPolicy(new ExponentialBackoffRetry(1000,3)) //重连策略
                .namespace("test")              //namespace代表命名空间
                .build();                       //建立连接通道

           //启动客户端    
           cc.start();

  retryPolicy,4种重连策略:

    //重连3次,每次休息3秒
    new RetryNTimes(3,3000);

    //重连3次,每次休息大约是1秒
    new ExponentialBackoffRetry(1000,3);

    //初始化一个大概的等待时间1秒,然后开始重连,最多重连3次,每次最多休息2秒
    new ExponentialBackoffRetry(1000,3,2000);

    //计算通过这个初始化的大约时间,计算实际需要睡眠多久
    long sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)));

2.2 创建结点

 

cc.create().creatingParentsIfNeeded()  // 创建父节点,也就是会递归创建
                .withMode(CreateMode.PERSISTENT)  // 节点类型(可以参考原API)
                .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)  // 节点的acl权限
                .forPath("/test/a/b", "data".getBytes());

注意:只有叶节点可以做临时节点,所以叶节点的父节点必须是永久节点,也就是creatingParentsIfNeeded这个方法创建的父节点必须是永久节点

2.3 删除结点和修改结点

修改结点:

Stat resultStat = cc.setData().withVersion(0)  // 指定数据版本
                         .forPath("/test", "123".getBytes());  // 需要修改的节点路径以及新数据

删除结点:

    cc.delete()
         .guaranteed()  // 如果删除失败,那么在后端还是会继续删除,直到成功
              .deletingChildrenIfNeeded()  // 子节点也一并删除,也就是会递归删除
                  .withVersion(resultStat.getVersion())  //删除版本
                       .forPath("/test");               //删除结点路径

2.4 获取某个结点的信息

获取某个节点的数据:

        // 读取节点数据
        Stat stat = new Stat();   //创建一个节点信息对象
        byte[] nodeData = cc.getData()
                               .storingStatIn(stat)  //闯入stat对象
                                      .forPath("/test");  //需要查找结点路径
        System.out.println("节点 " + nodePath + " 的数据为:" + new String(nodeData));
        System.out.println("该节点的数据版本号为:" + stat.getVersion());

获取某个节点下的子节点列表:

        // 获取子节点列表
        List<String> childNodes = cc.getChildren()   //表示获取子节点
                                        .forPath("/test"); //获取结点路径
        System.out.println(nodePath + " 节点下的子节点列表:");
        for (String childNode : childNodes) {
            System.out.println(childNode);
        }

查询某个节点是否存在:

Stat statExist = cc.checkExists() 
                    .forPath("/test");

2.5 curator使用usingWatcher(与原生API一次监听)

public class MyCuratorWatcher implements CuratorWatcher {

    // Watcher事件通知方法
    public void process(WatchedEvent watchedEvent) throws Exception {
        System.out.println("触发watcher,节点路径为:" + watchedEvent.getPath());
    }
}

    // 添加 watcher 事件,当使用usingWatcher的时候,监听只会触发一次,监听完毕后就销毁
    cc.getData()
           .usingWatcher(new MyCuratorWatcher()) //传入一个事件监听对象
                 .forPath("/test");              //监听的路径
       

2.6 curator使用nodeCache(一次注册N次监听)

想要实现watch一次注册n次监听的话,我们需要使用到curator里的一个NodeCache对象。这个对象可以用来缓存节点数据,并且可以给节点添加nodeChange事件,当节点的数据发生变化就会触发这个事件。

       // NodeCache: 缓存节点,并且可以监听数据节点的变更,会触发事件
        final NodeCache nodeCache = new NodeCache(cc, "/test");

        // 参数 buildInitial : 初始化的时候获取node的值并且缓存
        nodeCache.start(true);

        // 获取缓存里的节点初始化数据
        if (nodeCache.getCurrentData() != null) {
            System.out.println("节点初始化数据为:" + new 
                     String(nodeCache.getCurrentData().getData()));
        } else {
            System.out.println("节点初始化数据为空...");
        }

        // 为缓存的节点添加watcher,或者说添加监听器
        nodeCache.getListenable()
                 .addListener(new NodeCacheListener() {
            // 节点数据change事件的通知方法
            public void nodeChanged() throws Exception {
                // 防止节点被删除时发生错误
                if (nodeCache.getCurrentData() == null) {
                    System.out.println("获取节点数据异常,无法获取当前缓存的节点数据,可能该节点已被删除");
                    return;
                }
                // 获取节点最新的数据
                String data = new String(nodeCache.getCurrentData().getData());
                System.out.println(nodeCache.getCurrentData().getPath() + " 节点的数据发生变化,最新的数据为:" + data);
            }
        });

2.7curator使用PathChildrenCache 子节点监听

使用NodeCache虽然能实现一次注册n次监听,但是却只能监听一个nodeChanged事件,也就是说创建、删除以及子节点的事件都无法监听。如果我们要监听某一个节点的子节点的事件,或者监听某一个特定节点的增删改事件都需要借助PathChildrenCache来实现。从名称上可以看到,PathChildrenCache也是用缓存实现的,并且也是一次注册n次监听。当我们传递一个节点路径时是监听该节点下的子节点事件,如果我们要限制监听某一个节点,只需要加上判断条件即可。

// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
final PathChildrenCache childrenCache = new PathChildrenCache(cc, "/test", true);

        /**
         * StartMode: 初始化方式
         * POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
         * NORMAL:异步初始化
         * BUILD_INITIAL_CACHE:同步初始化
         */
        childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);

        // 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
        List<ChildData> childDataList = childrenCache.getCurrentData();
        System.out.println("当前节点的子节点详细数据列表:");
        for (ChildData childData : childDataList) {
            System.out.println("\t* 子节点路径:" + new String(childData.getPath()) + ",该节点的数据为:" + new String(childData.getData()));
        }

        // 添加事件监听器
        childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
                // 通过判断event type的方式来实现不同事件的触发
                if (event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)) {  // 子节点初始化时触发
                    System.out.println("\n--------------\n");
                    System.out.println("子节点初始化成功");
                } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)) {  // 添加子节点时触发
                    System.out.println("\n--------------\n");
                    System.out.print("子节点:" + event.getData().getPath() + " 添加成功,");
                    System.out.println("该子节点的数据为:" + new String(event.getData().getData()));
                } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {  // 删除子节点时触发
                    System.out.println("\n--------------\n");
                    System.out.println("子节点:" + event.getData().getPath() + " 删除成功");
                } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {  // 修改子节点数据时触发
                    System.out.println("\n--------------\n");
                    System.out.print("子节点:" + event.getData().getPath() + " 数据更新成功,");
                    System.out.println("子节点:" + event.getData().getPath() + " 新的数据为:" + new String(event.getData().getData()));
                }
            }
        });

2.8 curator之acl权限操作

创建权限:

       // 自定义权限列表
        List<ACL> acls = new ArrayList<ACL>();
        Id user1 = new Id("digest", AclUtils.getDigestUserPwd("user1:123456a"));
        Id user2 = new Id("digest", AclUtils.getDigestUserPwd("user2:123456b"));
        acls.add(new ACL(ZooDefs.Perms.ALL, user1));
        acls.add(new ACL(ZooDefs.Perms.READ, user2));
        acls.add(new ACL(ZooDefs.Perms.DELETE | ZooDefs.Perms.CREATE, user2));

        // 创建节点,使用自定义权限列表来设置节点的acl权限
        byte[] nodeData = "child-data".getBytes();
        cc.create()
          .creatingParentsIfNeeded() 
             .withMode(CreateMode.PERSISTENT)
                .withACL(acls)  
                    .forPath("/test/hello", "你好".getBytes());

        如果想要在递归创建节点时,父节点和子节点的acl权限都是我们自定义的权限,
        那么就需要在withACL方法中,传递一个true,表示递归创建时所有节点的权限,
        都是我们设置的权限。
        // 创建节点,使用自定义权限列表来设置节点的acl权限
        byte[] nodeData = "child-data".getBytes();
        cc.create()
           .creatingParentsIfNeeded()
              .withMode(CreateMode.PERSISTENT)
                 .withACL(acls, true)
                     .forPath("/test/hello", "你好".getBytes());

修改一个已存在的节点的acl权限:


        // 自定义权限列表
        List<ACL> acls = new ArrayList<ACL>();
        Id user1 = new Id("digest", AclUtils.getDigestUserPwd("user1:123456a"));
        Id user2 = new Id("digest", AclUtils.getDigestUserPwd("user2:123456b"));
        acls.add(new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.CREATE | ZooDefs.Perms.ADMIN, user1));
        acls.add(new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.DELETE | ZooDefs.Perms.CREATE, user2));

        // 设置指定节点的acl权限
        cc.setACL().withACL(acls).forPath("/test/hello");

最后,详细代码的实现演示可以参考

https://blog.51cto.com/zero01/2109139

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章