Zookeeper客戶端curator使用詳解

簡介

curator是Netflix公司開源的一個Zookeeper客戶端框架,curator框架在Zookeeper原生的API接口上進行了封裝,屏蔽了Zookeeper原生客戶端非常底層的細節開發,使得使用更加方便。並且還提供了Zookeeper多種應用場景的封裝,比如分佈式鎖服務、集羣領導選舉等場景實現的封裝,還實現了Fluent風格的鏈式調用API,是最好用,最流行的Zookeeper客戶端。

原生Zookeeper的不足:

  • 連接對象異步創建,需要開發人員自行編碼等待。
  • 連接沒有會話超時自動重連機制。
  • Watcher一次註冊只生效一次。
  • 不支持遞歸創建樹形節點。

curator特點:

  • 設有Session超時重連機制。
  • Watcher重複註冊機制。
  • 簡化開發API。
  • 遵循fluent風格API。
  • 提供Zookeeper常用的場景封裝實現。

依賴:

  <!--這個包是curator提供的對各種常用場景的封裝實現,比如分佈式鎖、集羣master選舉等-->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>4.2.0</version>
    </dependency>
    <!--這個包是curator提供對Zookeeper的基本操作封裝-->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>4.2.0</version>
    </dependency>

實戰demo:

curator對Zookeeper的基本操作是通過CuratorFramework接口來定義的。

curator會話連接超時重試策略

curator提供了會話連接超時的重試策略,用RetryPolicy接口定義。
常用的會話連接超時重試策略實現有:

//重試N次,當會話超時出現後,curator會每間隔sleepMsBetweenRetries毫秒時間重試一次,共重試n次。
RetryNTimes(int n, int sleepMsBetweenRetries)

//重試一次,是RetryNTimes的一種特殊實現,相當於RetryNTimes(1, int sleepMsBetweenRetries),
// 會話超時後sleepMsBetweenRetry毫秒後進行一次連接重試,僅重試一次。
public RetryOneTime(int sleepMsBetweenRetry)

//在maxElapsedTimeMs毫秒時間內,每隔sleepMsBetweenRetries重試一次。
//比如maxElapsedTimeMs=10000 sleepMsBetweenRetries=3000 ,表示在10秒內,每隔3秒重試一次,理論上是重試3次。
//重試次數有上限,是int類型的最大值次數。
public RetryUntilElapsed(int maxElapsedTimeMs, int sleepMsBetweenRetries)

//衰減重試,baseSleepTimeMs是基礎衰減時間,maxRetries是最大重試次數。
//重試時間間隔 = baseSleepTimeMs*math.max(1,random.nextInt(1<<(retryCount+1)))
//retryCount從0開始
//比如ExponentialBackoffRetry(1000,5),
//那麼第一次的重試時間間隔=1000*math.max(1,random.nextInt(1<<(1))),重試的時間間隔隨着重試的次數增大而增大,衰減重試,但是retryCount有最大值,就是當retryCount>29時,curator會把他設置成29。因爲1最多左移30位。最大間隔時間也有限制,默認int的最大值,也可以通過另一個構造函數來設置。
//只會重試maxRetries次。
public ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries)

//maxSleepMs最大間隔時間,當衰減重試計算的間隔時間比它大的,就設置爲該時間間隔。
public ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries, int maxSleepMs)

創建客戶端會話,實際上就是創建CuratorFramework對象。有兩種創建風格,普通風格跟fluent風格。

public class CuratorSessionDemo{

    //Zookeeper服務器地址,集羣的話多個用逗號分隔
    private static final String SERVER_STRING = "192.168.18.137:2181";

    //父級節點路徑
    private static final String PREFIX_PATH = "curator";

    private static  RetryPolicy retryOneTimePolicy = new RetryOneTime(3000);

    private static  RetryPolicy retryThreeTimePolicy = new RetryNTimes(3,3000);

    private static RetryPolicy retryUntilElapsed = new RetryUntilElapsed(10000,3000);

    private static RetryPolicy exponentialBackoffRetry = new ExponentialBackoffRetry(1000,3);

    private static List<AuthInfo> authInfoList = new ArrayList<>();

    static {
        AuthInfo authInfo = new AuthInfo("digest","admin:123456".getBytes());
        authInfoList.add(authInfo);
    }

    @Test
    public  CuratorFramework getClientWithNormalStyle(){
        /**
         * 普通方式創建Zookeeper的curator客戶端
         *參數一   Zookeeper服務器地址,多個用逗號分隔
         * 參數二  客戶端的會話超時  單位毫秒
         * 參數三: 客戶端連接到服務器的連接超時時間 單位毫秒
         * 參數四: 會話超時重連策略
         */
        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(SERVER_STRING,5000,
                10000,exponentialBackoffRetry);

        //上面僅創建了客戶端。必須要使用start方法來進行連接
        curatorFramework.start();
        
       return curatorFramework;
    }

    public CuratorFramework getClientWithFluentStyle(){
        CuratorFramework curatorFramework =
                //創建一個curatorFramework構造器
                CuratorFrameworkFactory.builder()
                  //設置Zookeeper服務器地址,多個用逗號分隔
                .connectString(SERVER_STRING)
                 //設置連接超時時間
                .connectionTimeoutMs(10000)
                  //設置會話超時時間
                .sessionTimeoutMs(5000)
                //設置數據壓縮的壓縮器
                .compressionProvider(new GzipCompressionProvider())
                  //設置會話超時重連策略
               
                .retryPolicy(exponentialBackoffRetry)
                  //添加認證
                .authorization(authInfoList)
                 //設置該客戶端是否只進行非事務操作
                .canBeReadOnly(false)
                 //設置默認的數據,當創建節點時不提供節點數據的話,就使用該數據
                .defaultData("".getBytes())
                  //設置節點路徑的父節點路徑
                .namespace(PREFIX_PATH)
                        //構建curatorFramework對象
                .build();
        //上面僅創建了客戶端。必須要使用start方法來進行連接
        curatorFramework.start();
        return curatorFramework;
    }
}

常用API(不包括Watcher機制):
public class CuratorOperationDemo {

    //同步創建節點
    @Test
    public void createNodeSync() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        Stat stat = new Stat();
        //返回創建節點的路徑
        String path = curatorFramework
                //創建節點構建器CreateBuilder
                .create()
                //把創建的節點的元信息保存在stat對象中
                .storingStatIn(stat)
                //遞歸創建
                .creatingParentsIfNeeded()
                //創建節點的類型,這裏選的是創建持久化節點。
                .withMode(CreateMode.PERSISTENT)
                //設置節點權限
                .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                //創建節點的路徑和數據
                .forPath("/node1", "node1".getBytes());
        System.out.println(path);
        curatorFramework.close();
    }

     //異步創建節點
    @Test
    public void createNodeASync() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //創建一個固定的線程數的線程池
        Executor executor = Executors.newFixedThreadPool(1);
        Stat stat = new Stat();
        //返回創建節點的路徑
        String path = curatorFramework
                //創建節點構建器CreateBuilder
                .create()
                //把創建的節點的元信息保存在stat對象中
                .storingStatIn(stat)
                //遞歸創建
                .creatingParentsIfNeeded()
                //創建節點的類型,這裏選的是創建持久化節點。
                .withMode(CreateMode.PERSISTENT)
                //設置節點權限
                .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                .inBackground(new BackgroundCallback() {
                    @Override
                    /**
                     * 節點創建操作成功的回調函數
                     * client  客戶端
                     * event  事件上下文
                     */
                    public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                        System.out.println("path = " + event.getPath());
                        System.out.println("context = " + event.getContext());
                        System.out.println("type = " + event.getType());
                    }
                },"刪除",executor)
                //創建節點的路徑和數據
                .forPath("/node3", "node3".getBytes());
        System.out.println(path);
        TimeUnit.SECONDS.sleep(10);
        curatorFramework.close();
    }

    //設置節點數據
    @Test
    public void setNodeData() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //返回設置節點後的節點元信息
        Stat stat = curatorFramework.setData()
                //對數據進行壓縮在設置,如果這裏設置了壓縮,獲取節點數據時就要進行相應的解壓縮操作。
                .compressed()
                .withVersion(-1)
                .forPath("/node3", "node33333".getBytes());
        System.out.println(stat);
        curatorFramework.close();
    }

    //刪除節點
    @Test
    public void deleteNode() throws Exception{
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        curatorFramework.delete()
                //遞歸刪除子節點
                .deletingChildrenIfNeeded()
                .withVersion(-1)
                .forPath("/node3");
                curatorFramework.close();
    }

    //獲取節點數據
    @Test
    public void getNodeData() throws Exception{
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        Stat stat = new Stat();
        //返回數據
        byte[] bytes = curatorFramework.getData()
                //解壓縮,如果設置數據時使用了壓縮,獲取數據時要進行相應的解壓縮進行數據還原
                .decompressed()
                //獲取節點元信息存在stat對象中
                .storingStatIn(stat)
                //添加監聽器
                .usingWatcher(new Watcher() {
                    @Override
                    public void process(WatchedEvent watchedEvent) {
                        System.out.println(watchedEvent.getPath());
                    }
                })
                .forPath("/node3");
                curatorFramework.close();
    }
    
    //獲取和設置節點的權限列表
    @Test
    public void aclDemo() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //設置acl
        Stat stat = curatorFramework.setACL()
                .withVersion(-1)
                .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                .forPath("/node3");
        //獲取節點acl
        List<ACL> acls = curatorFramework.getACL().forPath("/node3");
        System.out.println(acls);
        curatorFramework.close();
        
        
    }
    //獲取子節點列表
    @Test
    public void getNodeChildren() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        List<String> children = curatorFramework.getChildren()
                .forPath("/node3");
        System.out.println(children);
        curatorFramework.close();
        
    }
    
    //檢查節點是否存在
    @Test
    public void checkExist() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //返回null表示節點不存在,反正存在
        Stat stat = curatorFramework.checkExists()
                //當父節點不存在時創建父節點
                .creatingParentsIfNeeded()
                //添加監聽
                .usingWatcher(new Watcher() {
                    @Override
                    public void process(WatchedEvent watchedEvent) {
                        System.out.println(watchedEvent.getPath());
                    }
                })
                .forPath("/node3");
        System.out.println(stat);
        curatorFramework.close();
    }
}

異步操作的inBackground方法詳解:
以上的操作都有異步模式,上面在創建節點時演示了一下,其他操作也差不多。

public interface Backgroundable<T>
{
	//此方法有5個重載   

	//啥也不設置
    public T inBackground();

    //提供一個上下文對象,其實這個用的也不多。
    public T inBackground(Object context);

   
   	//傳入一個回調對象BackgroundCallback,會在操作執行完成後執行該回調方法。
    public T inBackground(BackgroundCallback callback);

   //傳入一個回調對象BackgroundCallback和一個上下文對象context,會在操作執行完成後執行該回調方法。
    public T inBackground(BackgroundCallback callback, Object context);

    //傳入一個回調對象BackgroundCallback和一個線程池對象,會用該線程池裏面的線程進行相關操作,比如異步創建節點是使用這個線程池中的線程異步創建的。
    public T inBackground(BackgroundCallback callback, Executor executor);

    //傳入一個回調器、上下文對象、線程池。
    public T inBackground(BackgroundCallback callback, Object context, Executor executor);
}


//回調器
public interface BackgroundCallback
{
    /**
     * 回調方法,我們就是通過實現該接口複寫自己的回調方法
     * client  CuratorFramework 客戶端對象
     * event事件上下文
     */
    public void processResult(CuratorFramework client, CuratorEvent event) throws Exception;
}

//CuratorEvent事件上下文,裏面的值不是每個事件類型都會有值,某些值只有特定的事件觸發纔會有。
public interface CuratorEvent
{
    /**
     * @return event type 事件類型
     */
    public CuratorEventType getType();

    /**
     * 操作執行的返回狀態碼可以用於判斷操作有沒有執行成功,與原生API回調的rc參數一致
     */
    public int getResultCode();

    /**
     * 節點路徑
     */
    public String getPath();

    /**
     * 節點上下文參數,就是inBackground方法傳遞的context。
     */
    public Object getContext();

    /**
     * 節點的元信息
     */
    public Stat getStat();

    /**
     * 節點的數據
     */
    public byte[] getData();

    
    public String getName();

    /**
     * 節點的子節點
     */
    public List<String> getChildren();

    /**
     * 節點的權限列表
     */
    public List<ACL> getACLList();

   
    public List<CuratorTransactionResult> getOpResults();

    /**
     * 節點的監聽事件
     */
    public WatchedEvent getWatchedEvent();
}



//curator的事件類型,會在調用不同的方法就會觸發相關的事件
public enum CuratorEventType
{
    /**
     * create方法事件
     */
    CREATE,

    /**
     *delete方法事件
     */
    DELETE,

    /**
     * checkExists方法的事件
     */
    EXISTS,

    /**
     * getData方法事件
     */
    GET_DATA,

    /**
     * setData方法事件
     */
    SET_DATA,

    /**
     * getChildren方法事件
     */
    CHILDREN,

    /**
     * sync方法事件
     */
    SYNC,

    /**
     * getACL方法事件
     */
    GET_ACL,

    /**
     * setACL方法事件
     */
    SET_ACL,

    /**
     * transaction方法事件
     */
    TRANSACTION,

    /**
     * getConfig方法事件
     */
    GET_CONFIG,

    /**
     * reconfig方法事件
     */
    RECONFIG,

    /**
     * watched方法事件
     */
    WATCHED,

    /**
     * watches方法事件
     */
    REMOVE_WATCHES,

    /**
     * close方法事件
     */
    CLOSING
}

curator的事務操作

curator還能進行事務操作,保證一段代碼的原子性。這是curator獨有的。

 @Test
    public void transactionNode() throws  Exception{
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //CuratorOp是對每個操作的抽象,這裏創建一個新增節點的操作
        //transactionOp() 這個方法返回事務操作類型 目前有create 、 check 、delete、setData
        CuratorOp createOp = curatorFramework.transactionOp().
                .create()
                .withMode(CreateMode.PERSISTENT)
                .forPath("node3", "555".getBytes());

        //這裏創建一個修改節點數據的操作
        CuratorOp setDataOp = curatorFramework.transactionOp()
                .setData()
                .withVersion(-1)
                .forPath("node4", "666".getBytes());

        //把操作添加進事務中,這兩個操作就具有原子性了
        //返回每個操作的結果封裝成CuratorTransactionResult對象
        List<CuratorTransactionResult> curatorTransactionResults = curatorFramework.transaction().forOperations(createOp, setDataOp);
        for (CuratorTransactionResult curatorTransactionResult:curatorTransactionResults){
            System.out.println(curatorTransactionResult);
        }

    }
curator的watcher實現:

curator有三種watcher來做節點監聽

  • PathChildrenCache:監視一個路徑下子節點的創建、刪除、節點數據的更新。
  • nodecache:監視一個節點的創建、刪除、更新。
  • treecache:pathcache + nodecache。緩存路徑下所有子節點的數據。
PathChildrenCachedemo
@Test
    public void testPathCache() throws Exception {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        //構造器有多個重載,這裏就實現參數比較多的一個
        /**
         * 第一個參數  客戶端對象
         * 第二個參數  要節點的節點路徑
         * 第三個參數  是否緩存子節點數據
         * 第四個參數  告知監聽器節點數據是否是壓縮數據
         * 第五個參數  用於PathChildrenCache的後臺線程的ExecutorService。此線程池應該是單線程的,否則緩存可能會看到不一致的結果。
         */
        PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework
                ,"/PathChildrenCache",
                true
                ,false
                ,executorService);

        //啓動監聽,必須要有這一步才能實現監聽,有三種啓動模式
        //PathChildrenCache.StartMode.NORMAL  異步進行緩存初始化
        //PathChildrenCache.StartMode.BUILD_INITIAL_CACHE  同步進行緩存初始化 並且會rebuild監聽器緩存
        //PathChildrenCache.StartMode.POST_INITIALIZED_EVENT 異步進行緩存初始化,並且會觸發PathChildrenCacheEvent.Type.INITIALIZED事件
        pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

        
        
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                switch (event.getType()){
                    case CHILD_ADDED:{
                        System.out.println("子節點被創建了");
                        System.out.println(event.getData());
                        System.out.println(event.getInitialData());
                        System.out.println(event.getType());
                        System.out.println(event.toString());
                        break;
                    }
                    case CHILD_REMOVED:{
                        System.out.println("子節點被刪除了");
                        System.out.println(event.getData());
                        System.out.println(event.getInitialData());
                        System.out.println(event.getType());
                        System.out.println(event.toString());
                        break;
                    }
                    case CHILD_UPDATED:{
                        System.out.println("子節點被修改了");
                        System.out.println(event.getData());
                        System.out.println(event.getInitialData());
                        System.out.println(event.getType());
                        System.out.println(event.toString());
                        break;
                    }
                    default:
                        break;

                }

            }
        });

        //獲取緩存中的子節點數據
        System.out.println(pathChildrenCache.getCurrentData());
        //創建子節點
        String path = curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/PathChildrenCache/node4", "node4".getBytes());
        TimeUnit.SECONDS.sleep(1);
        //修改子節點
        Stat stat = curatorFramework.setData().forPath("/PathChildrenCache/node4", "node100".getBytes());
        TimeUnit.SECONDS.sleep(1);
        //刪除子節點
        curatorFramework.delete().forPath("/PathChildrenCache/node4");

        System.out.println("okokokokokok");
        //阻塞主線程
        TimeUnit.SECONDS.sleep(3000);

    }

結果:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

NodeCache demo
 @Test
    public void testNodeCache() throws Exception {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();

        /**
         * 第一個參數  curator客戶端
         * 第二個參數 NodeCache
         * 第三個參數  節點數據是否被壓縮過
         */
        NodeCache nodeCache = new NodeCache(curatorFramework,"/NodeCache5",false);

        //如果爲true,就會在start前調用rebuild方法。
        nodeCache.start(true);

        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                //節點的創建 刪除  數據修改都會觸發,但是這裏卻沒有分事件類型
                System.out.println("nodeChanged觸發");
                System.out.println("節點數據修改後的結果:" + nodeCache.getCurrentData());
                System.out.println("節點路徑: " + nodeCache.getPath());
            }
        });

        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/NodeCache5","555".getBytes());
        TimeUnit.SECONDS.sleep(1);
        curatorFramework.setData().forPath("/NodeCache5","NodeCache5".getBytes());
        TimeUnit.SECONDS.sleep(1);
        curatorFramework.delete().forPath("/NodeCache5");

        TimeUnit.SECONDS.sleep(3000);

    }

結果:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

TreeCache

這個是最強大的Watcher

@Test
    public void testTree() throws Exception {
        CuratorFramework curatorFramework = CuratorSessionDemo.getClientWithFluentStyle();
        TreeCache treeCache = new TreeCache(curatorFramework,"/treeCache");
        treeCache.start();
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                switch (event.getType()){
                    case NODE_ADDED:{
                        System.out.println("節點"+ event.getData().getPath() +"被創建了");
                        System.out.println("新增的節點的數據:" + new String(event.getData().getData()));
                        break;
                    }
                    case NODE_REMOVED:{
                        System.out.println("節點"+ event.getData().getPath() +"被刪除了");
                        System.out.println("刪除的節點的數據:" + new String(event.getData().getData()));
                        break;
                    }
                    case NODE_UPDATED:{
                        System.out.println("節點"+ event.getData().getPath() +"數據被修改了");
                        System.out.println("修改後的節點的數據:" + new String(event.getData().getData()));
                        break;
                    }
                }
            }
        });
        //創建節點
        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/treeCache","555".getBytes());
        TimeUnit.SECONDS.sleep(1);
        //修改節點數據
        curatorFramework.setData().forPath("/treeCache","treeCache".getBytes());
        //創建節點的子節點
        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/treeCache/treeCache1","555".getBytes());
        //創建節點的子節點
        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/treeCache/treeCache2","555".getBytes());
        //創建節點的子節點的子節點
        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/treeCache/treeCache1/treeCache1.1","555".getBytes());

        //修改子節點的數據
        curatorFramework.setData().forPath("/treeCache/treeCache2","treeCache2".getBytes());
        TimeUnit.SECONDS.sleep(1);

        //獲取本節點treeCache的數據
        ChildData treeCacheData = treeCache.getCurrentData("/treeCache");
        System.out.println(treeCacheData.getPath() + "節點的數據是" + new String(treeCacheData.getData()));

        //獲取子節點的數據treeCache2
        ChildData treeCache2Data = treeCache.getCurrentData("/treeCache/treeCache2");
        System.out.println(treeCache2Data.getPath() + "節點的數據是" + new String(treeCache2Data.getData()));

        //獲取子節點的子節點treeCache1.1的數據
        ChildData treeCache1_1Data = treeCache.getCurrentData("/treeCache/treeCache1/treeCache1.1");
        System.out.println(treeCache1_1Data.getPath() + "節點的數據是" + new String(treeCache1_1Data.getData()));

        //獲取本節點的子節點
        Map<String, ChildData> currentChildren = treeCache.getCurrentChildren("/treeCache");
        System.out.println("節點/treeCache的子節點列表============子節點列表");
        for (Map.Entry<String,ChildData> entry: currentChildren.entrySet()){
            System.out.println("key = " + entry.getKey() + "data = " + new String(entry.getValue().getData()));
        }

        //獲取本節點的子節點的子節點列表
        Map<String, ChildData> currentChildren1 = treeCache.getCurrentChildren("/treeCache/treeCache1");
        System.out.println("節點/treeCache/treeCache1的子節點列表============子節點列表");
        for (Map.Entry<String,ChildData> entry: currentChildren1.entrySet()){
            System.out.println("key = " + entry.getKey() + "data = " + new String(entry.getValue().getData()));
        }

        //刪除節點/treeCache及其子節點
        curatorFramework.delete().deletingChildrenIfNeeded().forPath("/treeCache");



        TimeUnit.SECONDS.sleep(3000);
    }

結果:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
後面的刪除就不貼出來了 。
總結 :
TreeCache能夠監控 節點的新建/刪除/數據修改 、節點的子節點的新建/刪除/修改、節點的子節點的子節點的新建/刪除/修改,還有能緩存節點及其子節點、子節點的子節點到本地,實屬強大。

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