前言
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 |
None |
客戶端與服務端成功建立連接 |
此時客戶端和服務器處於連接狀態 |
|
NodeCreated(1) |
Watcher監聽的對應數據節點被創建 |
Create |
||
NodeDeleted |
Watcher監聽的對應數據節點被刪除 |
Delete/znode
|
||
NodeDataChanged |
Watcher監聽的對應數據節點的數據內容發生變更 |
setDate/znode |
||
NodeChildChanged |
Wather監聽的對應數據節點的子節點列表發生變更 |
Create/child |
||
Disconnected |
None |
客戶端與ZooKeeper服務器斷開連接 |
此時客戶端和服務器處於斷開連接狀態 |
|
Expired |
None |
會話超時 |
此時客戶端會話失效,通常同時也會受到SessionExpiredException異常 |
|
AuthFailed |
None |
通常有兩種情況,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