zookeeper客戶端


session會話機制

client請求和服務端建立連接,服務端會保留和標記當前client的session,包含session過期時間,sessionId,然后服務端開始在session過期時間的基礎上倒計時,在這段時間內,client需要向server發送心跳包,目的是讓server重置session過期時間

使用quit命令,退出客戶端,但是server端的session不會立即消失,使用ls / 依然可以看到創建的臨時節點

節點的類型:

  • 持久節點,不加任何參數,默認創建的是持久節點
  • 臨時節點: 當客戶端斷開連接時,這個節點會消失
  • 持久順序節點: 父節點為他的第一級子節點維護一份時序,標記節點的創建順序,這個標記其實就是一個數字后綴,作為新節點的名字,數字的范圍就是整形的最大值(1024*1024)
  • 臨時順序節點,同上. (臨時節點不能再創建的節點)

創建節點時,可以指定每個節點的data信息,zookeeper默認每個節點的數量的最大上限是1M

常用命令

創建節點:

語法 : create /path data

[zk: localhost:2181(CONNECTED) 2] create /changwu 1
Created /changwu
[zk: localhost:2181(CONNECTED) 3] ls /
[changwu, zookeeper]

創建順序節點

語法create -s /path data

[zk: localhost:2181(CONNECTED) 9] create -s /seq1 1
Created /seq10000000001
[zk: localhost:2181(CONNECTED) 10] ls /
[changwu, zookeeper, seq10000000001]

創建臨時節點 (extra)

語法: create -e /path data

[zk: localhost:2181(CONNECTED) 16] create -e /extra1 1
Created /extra1
[zk: localhost:2181(CONNECTED) 17] ls /
[changwu, extra1, zookeeper, seq10000000001]

當客戶端斷開連接時,臨時節點被刪除

獲取節點和節點指定的元數據

語法: get /path

[zk: localhost:2181(CONNECTED) 22] get /changwu
1  # data 源數據
cZxid = 0x200000008
ctime = Tue Sep 17 12:06:40 CST 2019
mZxid = 0x200000008
mtime = Tue Sep 17 12:06:40 CST 2019
pZxid = 0x200000008
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0

訪問臨時節點

注意點,訪問順序節點,需要帶上 節點的全稱

[zk: localhost:2181(CONNECTED) 37] get /seq10000000001
1
cZxid = 0x200000009
ctime = Tue Sep 17 12:08:16 CST 2019
mZxid = 0x200000009
mtime = Tue Sep 17 12:08:16 CST 2019
pZxid = 0x200000009
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0

設置元數據

語法: set /path data

[zk: localhost:2181(CONNECTED) 51] ls / 
[changwu, extra1, zookeeper, seq10000000001]
[zk: localhost:2181(CONNECTED) 52] set /changwu 2 
# 創建znode的事務id
cZxid = 0x200000008 
# znode的創建時間
ctime = Tue Sep 17 12:06:40 CST 2019
# 最后修改的znode的事務id
mZxid = 0x20000000b
# znode的最近修改的時間
mtime = Tue Sep 17 12:15:35 CST 2019
# 最后修改/刪除/添加znode的事務id
pZxid = 0x200000008
# 對當前znode 子節點 的修改次數
cversion = 0
# 對當前znode節點data的修改的次數
dataVersion = 1
# 對znode的acl修改的次數
aclVersion = 0
# 如果znode是(ephemeral短暫)類型節點,這就是znode所有者的sessionId
# 如果不是(ephemeral短暫)類型,設置為0
ephemeralOwner = 0x0
# znode數據字段的長度
dataLength = 1
# znode子節點數量
numChildren = 0

創建子節點

注意點,創建子節點時添加上父節點的路徑
語法: create / 父節點/子節點 子節點data

[zk: localhost:2181(CONNECTED) 67] create /changwu/child1 firstchild 
Created /changwu/child1

列出子節點

語法: ls /父節點

[zk: localhost:2181(CONNECTED) 70] ls /changwu
[child1]

查看znode的狀態

語法: stat /path

[zk: localhost:2181(CONNECTED) 73] stat /changwu
# 創建znode節點的事務id
cZxid = 0x200000008
# znode節點的創建時間
ctime = Tue Sep 17 12:06:40 CST 2019
# 最后修改znode的事務id
mZxid = 0x20000000b
# znode的最后修改時間
mtime = Tue Sep 17 12:15:35 CST 2019
# 相對於根節點來說, 最后修還,添加.刪除 znode節點的事務id
pZxid = 0x20000000c
# 對當前znode子節點更改的次數
cversion = 1
# 對znode data更改的次數
dataVersion = 1
# 對znode acl 更改的次數
aclVersion = 0
# 如果znode是epemeral類型的節點,則當前值就是znode所有者的sessionid
# 如果znode 不是 ephemeral 類型的,當前值=0
ephemeralOwner = 0x0
# znode data長度
dataLength = 1
# 子節點的數量
numChildren = 1

移除節點1

語法: delete /path
如果當前節點存在子節點,delete 是刪除不掉它的

移除節點2

語法: rmr /path

[zk: localhost:2181(CONNECTED) 87] rmr /
changwu          extra1           zookeeper        seq10000000001
[zk: localhost:2181(CONNECTED) 87] rmr /extra1
[zk: localhost:2181(CONNECTED) 88] ls /
[changwu, zookeeper, seq10000000001]


什么是ACL(access control list 訪問控制列表)

zookeeper在分布式系統中承擔中間件的作用,它管理的每一個節點上可能都存儲這重要的信息,因為應用可以讀取到任意節點,這就可能造成安全問題,ACL的作用就是幫助zookeeper實現權限控制

  • zookeeper的權限控制基於節點,每個znode可以有不同的權限
  • 子節點不會繼承父節點的權限, 訪問不了子節點,不代表訪問不到子節點

寫法: schema🆔permission

Schema: 鑒權的策略

schema 描述
world world代表只有一個用戶,anyone 代表全部用戶
ip 使用ip地址認證
auth 使用已經添加的認證用戶認證
disgest 使用"用戶名:密碼"方式認證

授權對象 ID

權限授予的用戶或實體

權限模式 授權對象
Ip 通常是一個Ip地址或者是Ip段, 例如192.168.0.1 或者 192.168.0.1/12
Digest 自定義, 通常是username:BASE64(SHA-1(username:password)) 例如"foo:uigfuagsfyuagdfyuagdfdalhgf="
World 只有一個ID 就是 anyone
Super 與digest模式一樣

權限Permission

權限 簡寫 描述
delete d 僅可以刪除子節點
create c 可以創建子節點
read r 讀取當前節點數據,及子節點列表
write w 設置節點數據
admin a 設置節點的訪問控制列表

查看ACL

語法getAcl /parentpath

[zk: localhost:2181(CONNECTED) 94] getAcl /changwu
'world,'anyone
: cdrwa

設置ACL: 客戶端實例

[zk: localhost:2181(CONNECTED) 102] setAcl /changwu world:anyone:wa
cZxid = 0x200000008
ctime = Tue Sep 17 12:06:40 CST 2019
mZxid = 0x20000000b
mtime = Tue Sep 17 12:15:35 CST 2019
pZxid = 0x20000000c
cversion = 1
dataVersion = 1
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 1
numChildren = 1

再次嘗試獲取,就沒有讀權限了,還掉了線

[zk: localhost:2181(CONNECTED) 115] get /changwu
Authentication is not valid : /changwu

設置ACL: auth方式

# 創建節點
[zk: localhost:2181(CONNECTED) 0] create /node2 2
Created /node2

# 添加一個用戶
[zk: localhost:2181(CONNECTED) 1] addauth digest lisi:123123
# 給這個node2節點設置一個;lisi的用戶,只有這個lisi才擁有node的全部權限
[zk: localhost:2181(CONNECTED) 2] setAcl /node2 auth:lisi:cdrwa
cZxid = 0x2d7
ctime = Fri Sep 27 08:19:58 CST 2019
mZxid = 0x2d7
mtime = Fri Sep 27 08:19:58 CST 2019
pZxid = 0x2d7
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0

[zk: localhost:2181(CONNECTED) 3] getAcl /node2
'digest,'lisi:dcaK2UREXUmcqg6z9noXkh1bFaM=
: cdrwa

這時候斷開客戶端的連接, 打開一個新的連接,重試get
# 會發現已經沒有權限了
[zk: localhost:2181(CONNECTED) 1] getAcl /node2
Authentication is not valid : /node2

# 重新添加auth
[zk: localhost:2181(CONNECTED) 2] addauth digest lisi:123123
[zk: localhost:2181(CONNECTED) 3] getAcl /node2
'digest,'lisi:dcaK2UREXUmcqg6z9noXkh1bFaM=
: cdrwa

設置ACL: Ip

setAcl /node2 ip:192.168.100.1:cdrwa #設置IP:192.168.100.1 擁有所有權限

設置ACL: Digest方案

  • Digest方案(添加supper也用這種方法)

它使用的密碼是經過SHA1和BASE64加密之后得到的,在shell 命令中用如下方法計算

echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64

[root@ecs-t6-large-2-linux-20190918000015 ~]# echo -n zhangsan:123456 | openssl dgst -binary -sha1 | openssl base64
jA/7JI9gsuLp0ZQn5J5dcnDQkHA=

例:

給node3添加認證
[zk: localhost:2181(CONNECTED) 9] setAcl /node4 digest:zhangsan:jA/7JI9gsuLp0ZQn5J5dcnDQkHA=

[zk: localhost:2181(CONNECTED) 3] get /node4
Authentication is not valid : /node4 #沒有權限
 
[zk: localhost:2181(CONNECTED) 4] addauth digest zhangsan:123456 #添加認證用戶
 
[zk: localhost:2181(CONNECTED) 5] get /node4

zookeeper的客戶端

原生客戶端 ZooKeeper

Zookeeper 存在一個問題,session有存在丟棄的可能性

client--網絡中斷-->server client和server通過網絡發送心跳保持溝通,如果因為網絡的原因,client發送的心跳包都沒有到達server端, server端的session倒計時機制就會把 session 和 watch全部清除

  • 原生zookeeper客戶端
  public static void main(String[] args) throws Exception {
   ZooKeeper client = new ZooKeeper("localhost", 5000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.err.println("連接,觸發");
            }
        });
  Stat stat = new Stat();
  • 創建客戶端
   /**
     * 1. path  
     * 2. data  
     * 3. acl 安全模式
     * 4. 持久化/臨時 策略
     */
  client.create("/node2","value of node2".getBytes(), (List<ACL>) ZooDefs.Ids.ANYONE_ID_UNSAFE,CreateMode.PERSISTENT);
  • 獲取節點數據
client.getData("/node1",true,stat);
  • 監聽回調
 String content = new String(  client.getData("/node1", new Watcher() {
            @Override
    public void process(WatchedEvent event) {
        // todo 任何連接上這個節點的客戶端修改了這個節點的 data數據,都會引起process函數的回調
        // todo 特點1:  watch只能使用1次
        if (event.getType().equals(Event.EventType.NodeDataChanged)){
            System.err.println("當前節點數據發生了改變");
        }
    }
}, new Stat()));
  • 非阻塞,直接回調
  client.getData("/node1", false, new AsyncCallback.DataCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
                System.out.println("123123");
            }
        },null);

Curator

  • 提供了多種zookeeper集群leader的選舉機制

  • 封裝了原生的zookeeper client 和 zookeeper server

  • Fluent Api 流式api

  • java API創建客戶端

    public static void main(String[] args) throws Exception {
        // 初始化客戶端
        // 總共重試三次,每次一秒
         CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181",new RetryNTimes(3,100));
         client.start();  // todo instance must be started before calling this method
        // 創建一個節點
        //client.create().withMode(CreateMode.PERSISTENT).forPath("/node3","value of node3".getBytes());

        // 訂閱
        String path = "/node1";
        
        /**
         *  todo NodeCache就是對這個path節點內存的緩存
         *  第一次執行會觸發, 而且,以后每次node被改變都會執行
         */
        NodeCache  cache = new NodeCache(client,path);

        // cache.start(true); 啟動時會同步數據到緩存中, 既然數據一致了,啟動時,下面的回調就不會觸發
        // cache.start(false); 默認是false, 不會觸發
        cache.start();
        cache.getListenable().addListener(new NodeCacheListener(){
            // todo 監聽節點值的改變
            @Override
            public void nodeChanged() {
                System.err.println("nodeChanged 執行了");
            }
        });
        
        System.in.read();
    }

也可以使用僅回調一次的api

// 和原生的客戶端一樣,僅僅觸發一次
 client.getData().usingWatcher(new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            System.err.println("process...");
        }
    }).forPath(path);

Curator解決session會話失效

    public static void main(String[] args) throws Exception {
         CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181",new RetryNTimes(3,100));
        client.start();  // todo instance must be started before calling this method

        client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
            @Override
            public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
                if(connectionState.equals(ConnectionState.LOST)){
                    // todo 重新連接
                }
            }
        });
        // todo dosomething
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM