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

 

 

 

 

 

 

 

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