Zookeeper--開源客戶端curator常用API

Curator

Curator是 Netflix公司開源的一套 ZooKeeper客戶端框架,作者是 Jordan Zimmerman和ZkClient一樣, Curator解決了很多 ZooKeeper客戶端非常底層的細節開發工作,包括連接重連、反覆註冊 Watcher和 NodeExistsException異常等,目前已經成爲了Apache的頂級項目,是全世界範圍內使用最廣泛的 ZooKeeper客戶端之一。

除了封裝- - 些開發人員不需要特別關注的底層細節之外,Curator 還在ZooKeeper 原生API的基礎上進行了包裝,提供了一套易用性和可讀性更強的Fluent風格的客戶端API框架。

除此之外,Curator中還提供了ZooKeeper 各種應用場景(Recipe,如共享鎖服務、Master選舉機制和分佈式計數器等)的抽象封裝。

Maven依賴:

		<dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.4.2</version>
        </dependency>

創建會話

工廠方法創建:

  1. 使用CuratorFrameworkFactory這個工廠類的兩個靜態方法來創建一個客戶端:

    • static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy);
    • static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy);
  2. 通過調用CuratorFramework中的start()方法來啓動會話

在這裏插入圖片描述

在重試策略上,Curator通過一個藉口RetryPolicy來讓用戶實現自定義的重試策略。

public interface RetryPolicy {
    boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper);
}

在這裏插入圖片描述

//使用Curator來創建一個Zookeeper客戶端
public class Create_Session_Sample {
    public static void main(String[] args) throws InterruptedException {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client =
                CuratorFrameworkFactory.newClient("127.0.0.1:2181",
                        5000,
                        3000,
                        retryPolicy);
        client.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

首先創建了一個名爲ExponentialBackoffRetry的重試策略,該重試策略是Curator默認提供的幾種重試策略之一一, 其構造方法如下:

  1. ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries);
  2. ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries, int maxSleepMs);

在這裏插入圖片描述

ExponentialBackoffRetry的重試策略如下:

給定一個初始sleep事件baseSleepTimeMs,在這個基礎上結合重試次數,通過以下公式計算出當前需要sleep的時間:

當前sleep時間 = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)))

可以看出,隨着重試次數的增加,計算出的sleep時間會越來越大。如果該sleep時間在maxSleepMs的範圍之內,那麼就使用該sleep時間,否則使用maxSleepMs。另外,maxRetries參數控制了最大重試次數,以避免無限制的重試。

使用Fluent風格的API接口創建會話:

//使用Fluent風格API創建一個Zookeeper客戶度
public class Create_Session_Sample_Fluent {
    public static void main(String[] args) throws InterruptedException {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .sessionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();
        client.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

使用Curator創建含隔離命名空間的會話:
爲了實現不同的ZooKeeper業務之間的隔離,往往會爲每個業務分配–個獨立的命名空間,即指定一個ZooKeeper根路徑。例如,下面所示的代碼片段中定義了某一個客戶端的獨立命名空間爲/base,那麼該客戶端對ZooKeeper.上數據節點的任何操作,都是基於該相對目錄進行的:

CuratorFrameworkFactoy.builder()
	.connectString("127.0.0.1:2181")
	.sessionTimeoutMs(5000)
	.retryPolicy(retryPolicy)
	.namespace("base")
	.build();

創建結點

CuratorFramework
	--public CreateBuilder create();
CreateBuilder
	--public ProtectACLCreateModePathAndBytesable<String>creatingParentsIfNeeded();
CreateModable
	--public T withMode(createMode mode);
PathAndBytesable<T>
	--public T forPath(String path, byte[] data) throws Exception;
	--public T forPath(String path) thorws Exception;

創建一個節點,初始內容爲空:

//注意,如果沒有設置節點屬性,那麼Curator默認創建的是持久節點,內容默認是空。
client.create().forPath("/zk-test");

在這裏插入圖片描述

創建一個結點,附帶初始內容:

//Curator按照Zookeeper原生API的風格,使用byte{]作爲方法參數
client.create().forPath("/zk-test1", "init".getBytes());

在這裏插入圖片描述

創建一個臨時結點,初始內容爲空:

//創建一個臨時結點,初始內容爲空
       client.create().withMode(CreateMode.EPHEMERAL).forPath("/zk-ephemeral");

創建一個臨時結點,並自動遞歸創建父節點:

//創建一個臨時結點,並自動遞歸創建父節點
        client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
                .forPath("/zk-parent/zk-son");

同時要注意的一點是,由於在ZooKeeper中規定了所有非葉子節點必須爲持久節點,調用上面這個API之後,只有path參數對應的數據節點是臨時節點,其父節點均爲持久節點。
在這裏插入圖片描述

刪除結點
CuratorFramework
	--public DeleteBuilder delete();
Versionable<T>
	--public T withVersion(int version);
DeleteBuilder
	--public DeleteBuilderBase guaranteed();
PathAndBytesable<T>
	--public T forPath(String path, byte[] data) throws Exception;
	--public T forPath(String path) thorws Exception;

刪除一個結點:

//刪除一個結點,只能刪除葉子結點
client.delete().forPath("/zk-parent");

刪除一個結點,並遞歸刪除其所有子節點:

//刪除一個結點,並遞歸刪除其所有子節點
        client.delete().deletingChildrenIfNeeded().forPath("/zk-test");

刪除一個結點,強制指定版本進行刪除:

//刪除一個結點,強制指定版本進行刪除
        client.delete().withVersion(0).forPath("/zk-test1");

刪除一個結點,強制保證刪除:

//刪除一個結點,強制保證刪除
        client.delete().guaranteed().forPath("/zk-delete");

注意,guaranteed()接口是一個保障措施,只要客戶端會話有效,那麼Curator會在後臺持續進行刪除操作,直到節點刪除成功。

讀取數據

CuratorFramework
	--public GetDataBuilder getData();
Statable<T>
	--public T storingStatIn(Stat stat);
Pathable<T>
	--public T forPath(String path) throws Exception

讀取一個結點的數據內容:

//返回值爲byte[]
client.getData().forPath(path);

讀取一個結點的數據內容,同時獲取到該結點的stat:

//通過傳入一箇舊的stat變量的方式來存儲服務端返回的最新的結點狀態信息
client.getData().storingStatIn(stat).forPath(path);

示例:

public class Get_Data_Sample {

    static String path = "/zk-book";
    static CuratorFramework client = CuratorFrameworkFactory.builder()
            .connectString("127.0.0.1:2181")
            .sessionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
            .build();

    public static void main(String[] args) throws Exception {
        client.start();
        client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(path, "init".getBytes());
        Stat stat = new Stat();
        System.out.println(new String(client.getData().storingStatIn(stat).forPath(path)));
    }
}

更新數據

CuratorFramework
	--public SetDataBuilder setData();
Versionable<T>
	--public T withVersion(int version);
PathAndBytesable<T>
	--public T forPath(String path, byte[] data) throws Exception;
	-- public T forPath(String path) throws Exception;

更新一個結點的數據內容:

//返回stat對象
client.setData().forPath(path);

更新一個結點的數據內容,強制指定版本進行更新:

//注意,withVersion接口就是用來實現CAS (Compare and Swap)的,version(版本信息)通常是從一箇舊的stat對象中獲取到的。
client.setData().withVersion(version).forPath(path);
public class Set_Data_Sample {
    static String path = "/zk-book";
    static CuratorFramework client = CuratorFrameworkFactory.builder()
            .connectString("127.0.0.1:2181")
            .sessionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
            .build();

    public static void main(String[] args) throws Exception {
        client.start();
        client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(path, "init".getBytes());
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath(path);

        System.out.println("Success set node for: " + path + ", new version:"
            + client.setData().withVersion(stat.getVersion()).forPath(path).getVersion());
        try {
            client.setData().withVersion(stat.getVersion()).forPath(path);
        } catch (Exception e) {
            System.out.println("Fail set node due to " + e.getMessage());
        }
    }
}
Success set node for: /zk-book, new version:1
Fail set node due to KeeperErrorCode = BadVersion for /zk-book

該程序前後進行了兩次更新操作,第一次使用最新的stat變量進行更新操作,更新成功;第二次使用了過期的stat變量進行更新操作,拋出異常: KeeperErrorCode =BadVersion。

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