1. 程式人生 > >Zookeeper客戶端對比選擇_4

Zookeeper客戶端對比選擇_4

del factor 異步 當前 lin pac call 深入 客戶端

Zookeeper客戶端對比選擇

本文思維導圖
技術分享圖片

使用框架的好處是自帶一套實用的API,但是Zookeeper雖然非常強大,但是社區卻安靜的可怕,版本更新較慢,下面會先從zookeeper原生API的不足說起,然後引出現在流行的開源客戶端工具。

1.原生API

  • 1.創建連接的時候是異步的,所以我們在開發的時候需要人工的寫代碼等待創建節點的狀態,如果需要的話。
  • 2.連接時無超時重連機制。本人覺得這個非常重要,因為在現實使用中,網絡是不可信的,在創建節點的時候要考慮到網絡的不穩定性。因此,超時重連機制是非常必要的。
  • 3.zookeepr的通信是網絡通信,因此在數據通信的時候會消耗一定的網絡IO和帶寬。但zookeeper沒有序列化機制,需要開發者自行開發。
  • 4.Watcher註冊一次,觸發後會自動失效。
  • 5.不支持遞歸創建樹形節點。這點是比較有用的,類似Linux的命令:mkdir -p /xxx/xxx/

基於以上的一些不足,引起了業界一些大佬的不滿,因此它們自行開發了一些開源的客戶端工具。比如ZkClient和Curator。對前者簡單介紹,現在使用最多的是後者。

2.ZkClient

2.1 ZkClient概述

ZkClient是Github上一個開源的zk客戶端,由datameer的工程師Stefan Groschupf和Peter Voss一起開發(最仰慕的就是這類大佬,類似Linus那樣,一不爽寫個開源版本(git)出來...)

  • 解決session會話超時重連。
  • Watcher反復註冊。
  • 簡化開發api。

當然還有很多的很多修改的功能,使用也很簡單,但是社區不活躍,連api文檔都不完善,對於我們來說只能看源碼來開發應用了,也略有麻煩的。有興趣的開源上github看看。 https://github.com/sgroschupf/zkclient

2.2 ZkClien API

  • 創建客戶端 ZkClient zkclient = new ZkClient("192.168.17.128:2181,192.168.17.129:2181,192.168.17.130:2181",5000);
  • 創建節點 zkclient.create(path, data, CreateMode.PERSISTENT);
  • 刪除節點 zkclient.delete(path);
  • 獲取子節點 zkclient.getChildren(path);
  • 關閉客戶端 zkclient.close();
  • 讀節點數據 zkclient.readData(path);
  • 判斷節點是否存在 zkclient.exists(path);

3.Curator

3.1 Curator概述

Curator是Apache基金會的頂級項目之一。Apache基金會就類似萬能儲備室,把全球頂級的開源項目收納其中,造福一方百姓啊。

  • 下面是Curator對比原生zk的API和ZkClient比較重要的完善點:
  • 解決session會話超時重連。
  • Watcher反復註冊。
  • 簡化開發api。
  • 遵循Fluent風格Api規範。
  • NodeExistsException異常處理。
  • ......

3.2 Curator API介紹

  • 創建會話
  • 1.使用CuratorFrameworkFactory工廠的兩個靜態方法創建客戶端
  • 2.Start()方法啟動

    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    client = CuratorFrameworkFactory.builder()
    .connectString("localhost:2181,localhost:2182")
    .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
    .namespace("base").build();
    client.start();

  • 1.重試策略(實現接口RetryPolicy可以自定義重試策略)
    boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper)

  • 2.默認四種重試策略: Exponential BackoffRetry、RetryNTimes、RetryOneTime、
    RetryUntilElapsed

  • 2.1 ExponentialBackoffRetry
  • ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries)
  • ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries, int maxSleepMs)
  • 當前應該sleep的時間: baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)))

  • 2.2 RetryNTimes
  • RetryNTimes(int n, int sleepMsBetweenRetries)

  • 2.3 RetryOneTime
  • RetryOneTime(int sleepMsBetweenRetry)

  • 2.4 RetryUntilElapsed
    RetryUntilElapsed(int maxElapsedTimeMs, int sleepMsBetweenRetries)

  • 3.Fluent風格的API
  • 定義:一種面向對象的開發方式,目的是提高代碼的可讀性
  • 實現方式?通過方法的級聯或者方法鏈的方式實現
  • 例子:

    client = CuratorFrameworkFactory.builder()
            .connectString("localhost:2181,localhost:2182")
            .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
            .namespace("base").build();
  • 4.常用API
  • 4.1創建節點

    public void createNode(String path, byte[] data) throws Exception {
        client.getZookeeperClient().getZooKeeper().addAuthInfo("digest", "test:123456".getBytes());
        client.create().creatingParentsIfNeeded()
                .withMode(CreateMode.PERSISTENT).withACL(Ids.CREATOR_ALL_ACL)
                .forPath(path, data);
    }
  • 4.2刪除節點

    public void deleteNode(String path, int version) throws Exception {
        client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(version)
                .inBackground(new DeleteCallBack()).forPath(path);
    }
  • 4.3讀取節點

    public void readNode(String path) throws Exception {
        byte[] data = client.getData().inBackground(new DeleteCallBack()).forPath(path);
        System.out.println(path + "的數據:" + new String(data));
    }
  • 4.4更新節點數據

    public void updateNode(String path, byte[] data, int version)
            throws Exception {
        client.setData().withVersion(version).inBackground(new DeleteCallBack()).forPath(path, data);
    }
  • 4.5獲取子節點

    public void getChildren(String path) throws Exception {
        List<String> children = client.getChildren().usingWatcher(new WatcherTest()).forPath("/test");
        for (String pth : children) {
            System.out.println("child=" + pth);
        }
    }

4.6為節點添加監聽

  • NodeCache
  • 監聽數據節點的內容變更
  • 監聽節點的創建,即如果指定的節點不存在,則節點創建後,會觸發這個監聽
  • PathChildrenCache
  • 監聽指定節點的子節點變化情況
  • 包括新增子節點,子節點數據變更和子節點刪除

    public void addNodeDataWatcher(String path) throws Exception {
        final NodeCache nodeC = new NodeCache(client, path);
        nodeC.start(true);
        nodeC.getListenable().addListener(new NodeCacheListener() {
            public void nodeChanged() throws Exception {
                String data = new String(nodeC.getCurrentData().getData());
                System.out.println("path=" + nodeC.getCurrentData().getPath()
                        + ":data=" + data);
            }
        });
    }

4.7異步回調

  • inBackground()
  • inBackground(Object context)
  • inBackground(BackgroundCallback callback)
  • inBackground(BackgroundCallback callback, Object context)
  • inBackground(BackgroundCallback callback, Executor executor)
  • inBackground(BackgroundCallback callback, Object context, Executor executor)

Curator的回調與zk的原生異步api相同,多了一個線程池,用於執行回調。

異步操作事件狀態:event.getType()
異步操作事件狀態碼:event.getResultCode()

以上就是Curator常用的API,都是利用這些API來進行更加復雜的應用開發的,比如分布式鎖,集群管理等應用。

4.小結

好的開源工具解放我們的開發,但是不要在其中迷失,還要深入的了解其中的設計原理,不能只是調用API,要學習人家的思想,這樣才能跟隨大神的腳步,提升自己的實力。

Zookeeper客戶端對比選擇_4