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
}