zookeeper核心znode、watcher、ACL

前言

GitHub:https://github.com/yihonglei/ZooKeeper-Study

本文采用zk原生客戶端方式對zk進行操作,對應github的zk-native項目。

maven引入jar包:

<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
</dependency>

一 znode

1、 znode概述

ZooKeeper操作和維護的一個個數據稱爲znode,採用類似文件系統的層級樹狀結構進行管理。

2、znode四種類型

持久無序(PERSISTENT):節點創建後,如果不手動刪除,節點一直存在,節點是無序的。

持久有序(PERSISTENT_SEQUENTIAL):持久,有序。

臨時無序(EPHEMERAL):客戶端session失效就會自動刪除節點,節點無序。

臨時有序(EPHEMERAL_SEQUENTIAL):臨時,有序,

在創建節點時,需要指定節點類型。

3、znode節點數據

ZooKeeper的Stat對象中記錄了節點數據,主要包括數據信息,版本,權限。

4、zkCli創建一個節點,查看數據

[zk: localhost:2181(CONNECTED) 33] create /lanhuigu 2019
Created /lanhuigu
[zk: localhost:2181(CONNECTED) 34] ls /lanhuigu
[]
[zk: localhost:2181(CONNECTED) 35] get /lanhuigu
2019
cZxid = 0x80000003b
ctime = Thu May 16 18:31:50 CST 2019
mZxid = 0x80000003b
mtime = Thu May 16 18:31:50 CST 2019
pZxid = 0x80000003b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

5、Stat數據結構說明

狀態屬性

說明

czxid

節點創建時的zxid

mzxid

節點最新一次更新發生時的zxid

ctime

節點創建時的時間戳

mtime

節點最新一次更新發生時的時間戳

dataVersion

 節點數據的更新次數

cversion

其子節點的更新次數

aclVersion

節點ACL(授權信息)的更新次數

ephemeralOwner

如果該節點爲ephemeral節點, ephemeralOwner值表示與該節點綁定的session id。 如果該節點不是ephemeral節點,ephemeralOwner值爲0。

dataLength

節點數據的字節數

numChildren

子節點個數

6、znode實例

ZooKeeper原生客戶端對znode進行crud操作。

package com.lanhuigu.zookeeper.znode;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * zookeeper使用原生方式連接,進行crud操作
 *
 * @auther: yihonglei
 * @date: 2019-05-11 21:43
 */
public class ZooKeeperCrud {
    private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";

    private ZooKeeper zk;

    public ZooKeeperCrud() {
        try {
            zk = new ZooKeeper(connectString, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("監聽器....");
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 創建持久無序節點
     */
    public String createPersistent(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    /**
     * 創建臨時無序節點
     */
    public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    }

    /**
     * 獲取信息
     */
    public String getData(String path) throws KeeperException, InterruptedException {
        byte[] data = zk.getData(path, false, null);
        data = (data == null) ? "null".getBytes() : data;
        return new String(data);
    }

    /**
     * 更新信息
     */
    public Stat setData(String path, String data) throws KeeperException, InterruptedException {

        return zk.setData(path, data.getBytes(), -1);
    }

    /***
     * 判斷節點是否存在
     */
    public Stat exists(String path) throws KeeperException, InterruptedException {

        return zk.exists(path, false);
    }

    /***
     * 刪除節點
     */
    public void delete(String path) throws KeeperException, InterruptedException {

        zk.delete(path, -1);
    }

    /***
     * 遞歸刪除節點
     */
    public void deleteRecursive(String path) throws KeeperException, InterruptedException {

        ZKUtil.deleteRecursive(zk, path);
    }

    /**
     * 測試代碼
     */
    public static void main(String[] args) throws KeeperException, InterruptedException {
        ZooKeeperCrud crud = new ZooKeeperCrud();

        if (null != crud.exists("/lanhuigu")) {
            crud.delete("/lanhuigu");

            // 如果節點下還有節點數據,需要遞歸刪除
            // crud.deleteRecursive("/lanhuigu");
        }

        crud.createPersistent("/lanhuigu", "2019");

        System.out.println(crud.getData("/lanhuigu"));
    }
}

7、代碼說明

1)判斷節點是否存在,如果存在,則刪除掉;

2)創建一個持久節點/lanhuigu,節點數據爲2019;

3)獲取節點數據;

二 watcher

Watcher是ZooKeeper的事件監聽器,ZooKeeper允許用戶註冊事件,當事件觸發時服務端會通知到客戶端,

客戶端可以做相應的處理,該機制是Zookeeper實現分佈式協調服務的重要特性。

KeeperState

EventType

觸發條件

說明

操作

SyncConnected
(3)

None
(-1)

戶端與服務端成功建立連接

此時客戶端和服務器處於連接狀態

 

NodeCreated(1)

Watcher監聽的對應數據節點被創建

Create

NodeDeleted
(2)

Watcher監聽的對應數據節點被刪除

Delete/znode

 

NodeDataChanged
(3)

Watcher監聽的對應數據節點的數據內容發生變更

setDate/znode

NodeChildChanged
(4)

Wather監聽的對應數據節點的子節點列表發生變更

Create/child

Disconnected
(0)

None
(-1)

客戶端與ZooKeeper服務器斷開連接

此時客戶端和服務器處於斷開連接狀態

 

Expired
(-112)

None
(-1)

會話超時

此時客戶端會話失效,通常同時也會受到SessionExpiredException異常

 

AuthFailed
(4)

None
(-1)

通常有兩種情況,1:使用錯誤的schema進行權限檢查 2:SASL權限檢查失敗

通常同時也會收到AuthFailedException異常

 

1、watche實例

package com.lanhuigu.zookeeper.watcher;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * zookeeper使用原生方式連接,watcher測試
 *
 * @auther: yihonglei
 * @date: 2019-05-11 21:43
 */
public class ZooKeeperWatcher implements Watcher {
    private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";

    private ZooKeeper zk;

    public ZooKeeperWatcher() {
        try {
            zk = new ZooKeeper(connectString, 5000, this);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 創建持久無序節點
     */
    public String createPersistent(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    /**
     * 創建臨時無序節點
     */
    public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    }

    /**
     * 獲取信息
     */
    public String getData(String path, boolean watcher) throws KeeperException, InterruptedException {
        byte[] data = zk.getData(path, watcher, null);
        data = (data == null) ? "null".getBytes() : data;
        return new String(data);
    }

    /**
     * 更新信息
     */
    public Stat setData(String path, String data) throws KeeperException, InterruptedException {

        return zk.setData(path, data.getBytes(), -1);
    }

    /***
     * 判斷節點是否存在
     */
    public Stat exists(String path, boolean watcher) throws KeeperException, InterruptedException {

        return zk.exists(path, watcher);
    }

    /***
     * 刪除節點
     */
    public void delete(String path) throws KeeperException, InterruptedException {

        zk.delete(path, -1);
    }

    /***
     * 遞歸刪除節點
     */
    public void deleteRecursive(String path) throws KeeperException, InterruptedException {

        ZKUtil.deleteRecursive(zk, path);
    }

    /**
     * Watcher處理
     */
    @Override
    public void process(WatchedEvent event) {
        // 連接狀態
        Event.KeeperState keeperState = event.getState();

        // 事件類型
        Event.EventType eventType = event.getType();

        // 受影響的path
        String path = event.getPath();

        System.out.println("連接狀態:" + keeperState + ",事件類型:" + eventType + ",受影響的path:" + path);

        try {
            if (null != this.exists("/lanhuigu", true)) {
                System.out.println("內容:" + this.getData("/lanhuigu", true));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("==================");
    }

    /**
     * 測試代碼
     */
    public static void main(String[] args) throws KeeperException, InterruptedException {
        ZooKeeperWatcher crud = new ZooKeeperWatcher();

        // 判斷節點是否存在,存在則刪除
        if (null != crud.exists("/lanhuigu", true)) {
            Thread.sleep(1000);

            crud.delete("/lanhuigu");

            // 注意:如果節點下還有節點數據,需要遞歸刪除
            // crud.deleteRecursive("/lanhuigu");
        }

        // 創建持久無序節點
        crud.createPersistent("/lanhuigu", "2019");

        // 主線程休眠10秒,讓子線程都執行完
        Thread.sleep(1000 * 1000);
    }
}

2、代碼說明

1)ZooKeeperWatcher實現watcher接口,並實現process方法;

2)構造器創建ZooKeeper時註冊監聽;

3)當節點改變時,觸發監聽;

三 ACL

1、ACL(Access Control List)

內置的 ACL schemes:

world:默認方式,相當於全世界都能訪問。


auth:代表已經認證通過的用戶(cli中可以通過addauth digest user:pwd 來添加當前上下文中的授權用戶)。


digest:即用戶名:密碼這種方式認證,這也是業務系統中最常用的。


ip:使用Ip地址認證。

 

2、ACL支持權限

CREATE: 能創建子節點。

READ:能獲取節點數據和列出其子節點。

WRITE: 能設置節點數據。

DELETE: 能刪除子節點。

ADMIN: 能設置權限。

 

3、查看節點所屬權限

[zk: localhost:2181(CONNECTED) 13] getAcl /zookeeper

'world,'anyone

: cdrwa

 

4、ACL實例

package com.lanhuigu.zookeeper.acl;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * zookeeper使用原生方式連接,進行acl測試
 *
 * @auther: yihonglei
 * @date: 2019-05-11 21:43
 */
public class ZooKeeperAcl {
    private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";

    private ZooKeeper zk;

    /**
     * 認證類型
     */
    private final static String scheme = "digest";
    private final static String auth = "root:123456";

    /**
     * flag 爲true權限認證,否則,非權限認證
     */
    public ZooKeeperAcl(boolean flag) {
        try {
            zk = new ZooKeeper(connectString, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("監聽器......");
                }
            });
            if (flag) {
                zk.addAuthInfo(scheme, auth.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 創建持久無序節點
     */
    public String createPersistent(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    /**
     * 創建持久無序節點(權限創建)
     */
    public String createPersistentAcl(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
    }

    /**
     * 創建臨時無序節點
     */
    public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {

        return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    }

    /**
     * 獲取信息
     */
    public String getData(String path) throws KeeperException, InterruptedException {
        byte[] data = zk.getData(path, false, null);
        data = (data == null) ? "null".getBytes() : data;
        return new String(data);
    }

    /**
     * 更新信息
     */
    public Stat setData(String path, String data) throws KeeperException, InterruptedException {

        return zk.setData(path, data.getBytes(), -1);
    }

    /***
     * 判斷節點是否存在
     */
    public Stat exists(String path) throws KeeperException, InterruptedException {

        return zk.exists(path, false);
    }

    /***
     * 刪除節點
     */
    public void delete(String path) throws KeeperException, InterruptedException {

        zk.delete(path, -1);
    }

    /***
     * 遞歸刪除節點
     */
    public void deleteRecursive(String path) throws KeeperException, InterruptedException {

        ZKUtil.deleteRecursive(zk, path);
    }

    /**
     * 測試代碼
     */
    public static void main(String[] args) throws KeeperException, InterruptedException {
        // 1、權限認證(flag = true)
        ZooKeeperAcl acl = new ZooKeeperAcl(true);

        String path = "/lanhuiguAcl";

        if (null != acl.exists(path)) {
            acl.delete(path);

            // 如果節點下還有節點數據,需要遞歸刪除
            // acl.deleteRecursive("/lanhuiguAcl");
        }

        acl.createPersistentAcl(path, "2019-acl");

        System.out.println("權限認證讀取:" + acl.getData(path));

        // 2、非權限認證(flag = false)
        ZooKeeperAcl noAcl = new ZooKeeperAcl(false);

        System.out.println("非權限認證讀取權限認證:" + noAcl.getData(path));

        // 命令客戶端權限認證訪問
        /**
         * [zk: localhost:2181(CONNECTED) 12] addauth digest root:123456
         * [zk: localhost:2181(CONNECTED) 13] get /lanhuiguAcl
         * 2019-acl
         * cZxid = 0x300000095
         * ctime = Sun May 12 22:26:19 CST 2019
         * mZxid = 0x300000095
         * mtime = Sun May 12 22:26:19 CST 2019
         * pZxid = 0x300000095
         * cversion = 0
         * dataVersion = 0
         * aclVersion = 0
         * ephemeralOwner = 0x0
         * dataLength = 8
         * numChildren = 0
         */
    }
}

5、代碼運行

6、代碼說明

1)在構造器設置權限級別,用戶名和密碼;

2)權限認證創建節點,權限認證能讀,但是非權限認證去讀的時候會報NoAuth錯誤;

3)zkCli訪問時需要授權訪問;

[zk: localhost:2181(CONNECTED) 12] addauth digest root:123456
[zk: localhost:2181(CONNECTED) 13] get /lanhuiguAcl
2019-acl
cZxid = 0x300000095
ctime = Sun May 12 22:26:19 CST 2019
mZxid = 0x300000095
mtime = Sun May 12 22:26:19 CST 2019
pZxid = 0x300000095
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8

 

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