本文內容包括:zookeeper的簡介、數據模型、單機安裝過程、常用Shell命令、ACL權限控制以及JavaAPI調用
傳送門(已完結):
認認真真學習zookeeper(一)
認認真真學習zookeeper(二)
認認真真學習zookeeper(三)
文章目錄
1. zookeeper簡介
1.1 什麼是zookeeper
zookeeper官網:https://zookeeper.apache.org/
ZooKeeper由雅虎研究院開發,是Google Chubby的開源實現,後來託管到Apache,於2010年11月正式成爲Apache的頂級項目。
大數據生態系統裏的很多組件的命名都是某種動物或者昆蟲,比如hadoop就是🐘,hive就是🐝。zookeeper即動物園管理者,顧名思義就是管理大數據生態系統各組件的管理員,如下圖所示:
1.2 zookeeper應用場景
zookeeper是一個經典的分佈式數據一致性解決方案,致力於爲分佈式應用提供一個高性能、高可用,且具有嚴格順序訪問控制能力的分佈式協調存儲服務。
- 維護配置信息
- 分佈式鎖服務
- 集羣管理
- 生成分佈式唯一ID
1.維護配置信息
java編程經常會遇到配置項,比如數據庫的url、schema、user和passwords等。通常這些配置項我們會放置在配置文件中,再將配置文件放置在服務器上當需要更改配置項時,需要去服務器上修改對應的配置文件。但是隨着分佈式系統的興起,由於許多服務都需要使用到該配置文件,因此有必須保證該配置服務的高可用性(high availability)和各臺服務器上配置數據的一致性。通第會將配置文件部署在一個集羣上,然而一個集羣動輒上千臺服務器,此時如果再一臺臺服務器逐個修改配置文件那將是非常繁瑣且危險的的操作,因此就需要一種服務,能夠高效快速且可靠地完成配置項的更改等操作,並能夠保證各配置項在每臺服務器上的數據一致性。
zookeeper就可以提供這樣一種服務,其使用Zab這種一致性協議來保證一致性。現在有很多開源項目使用zookeeper來維護配置,比如在hbase中,客戶端就是連接一個zookeeper,獲得必要的hbase集羣的配置信息然後纔可以進一步操作。還有在開源的消息隊列kafka中,也使用zookeeper來維護brokers的信息。在 alibaba開源的soa框架dubbo中也廣泛的使用zookeeper管理一些配置來實現服務治理。
2.分佈式鎖服務
一個集羣是一個分佈式系統,由多臺服務器組成。爲了提高併發度和可靠性,多臺服務器上運行着同一種服務。當多個服務在運行時就需要協調各服務的進度,有時候需要保證當某個服務在進行某個操作時,其他的服務都不能進行該操作,即對該操作進行加鎖,如果當前機器掛掉後,釋放鎖並fail over到其他的機器繼續執行該服務。
3.集羣管理
一個集羣有時會因爲各種軟硬件故障或者網絡故障,出現某些服務掛掉而被移除集羣,而某些服務器加入到集羣中的情況,zookeeper會將這些服務加入/移出的情況通知給集羣中的其他正常工作的服務器,以及時調整存儲和計算等任務的分配和執行等。此外zookeeper還會對故宣的服務器做出診斷並嘗試修復。
4.生成分佈式唯一ID
在過去的單庫單表型系統中,通常可以使用數據庫字段自帶的auto_increment屬性來自動爲每條記錄生成一個唯一的ID。但是分庫分表後,就無法在依靠數據庫的auto_increment屬性來唯一標識一條記錄了。此時我們就可以用zookeeper在分佈式環境下生成全局唯一ID。做法如下:每次要生成一個新id時,創建一個持久順序節點創建操作返回的節點序號,即爲新id,然後把比自己節點小的刪除即可
1.3 zookeeper的設計目標
zookeeper致力於爲分佈式應用提供一個高性能、高可用,且具有嚴格順序訪問控制能力的分佈式協調服務
1.高性能
zookeeper將全量數據存儲在內存中,並直接服務於客戶端的所有非事務請求,尤其適用於以讀爲主的應用場景
2.高可用
zookeeper一般以集羣的方式對外提供服務,一般3~5臺機器就可以組成一個可用的zookeeper集羣了,每臺機器都會在內存中維護當前的服務器狀態,並且每臺機器之間都相互保持着通信。只要集羣中超過一半的機器都能夠正常工作,那麼整個集羣就能夠正常對外服務
3.嚴格順序訪問
對於來自客戶端的每個更新請求,ZooKeeper都會分配一個全局唯一的遞增編號,這個編號反映了所有事務操作的先後順序
2. zookeeper的數據模型
zookeeper的數據節點可以視爲樹狀結構(或者目錄),樹中的各節點被稱爲znode(即zookeeper node),一個znodes可以有多個子節點。zookeeper節點在結構上表現爲樹狀;使用路徑path來定位某個znode,比如/ns-1/itcast/mysql/schema1/table1,此處ns-1、itcast、mysql、schema1、table1分別是根節點、2級節點、3級節點以及4級節點;其中ns-1是itcast的父節點,,itcast是ns-1的子節點。itcast是mysq的父節點,mysq是itcast的子節點,以此類推。
znode,兼具文件和目錄兩種特點。既像文件一樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄一樣可以作爲路徑標識的一部分。
那麼如何描述一個znode?一個znode大體上分爲3個部分:
- 節點的數據:即znode data(節點path,節點data)的關係就像是java map中(key, value)的關係
- 節點的子節點children
- 節點的狀態stat:用來描述當前節點的創建、修改記錄,包括cZxid、ctime等
節點狀態stat的屬性
在zookeeper shell中使用get命令查看指定路徑節點的data、stat信息:
[ zk : localhost:2181 (CONNECTED) 7 ] get /ns-1/tenant
czxid = 0x6a0000000a
ctime = wed Mar 27 09:56:44 CST 2019
mZxid = 0x6a0000000a
mtime = Wed Mar 27 09:56:44 CST 2019
pZxid = 0x6a0000000e
cversion = 2
dataVersion = 0
aclversion = 0
ephemer alowner = 0x0
dataLength = 0
numChildren = 2
屬性說明:
- cZxid:數據節點創建時的事務ID
- ctime:數據節點創建時的時間
- mZxid:數據節點最後一次更新時的事務ID
- mtime:數據節點最後一次更新時的時間
- pZxid:數據節點的子節點最後一次被修改的事務ID
- cversion:子節點的更改次數
- dataVersion:節點數據的更改次數
- aclVersion:節點的ACL的更改次數
- ephemer alowner:如果節點是臨時節點,則表示創建該節點的會話的SessionID;如果節點是持久節點,則該屬性爲0
- dataLength:數據內容的長度
- numChildren:數據節點當前的子節點個數
節點類型
zookeeper中的節點有兩種,分別爲臨時節點和永久節點。節點的類型在創建時即被確定,並且不能改受。 - 臨時節點:該節點的生命週期依賴於創建它們的會話。一旦會話(Session)結束,臨時節點將被自動刪除,當然可以也可以手動刪除。雖然每個臨時的Znode都會綁走到一個客戶端會話,但他們對所有的客戶端還是可見的。另外,ZooKeeper的臨時節點不允許擁有子節點。
- 持久化節點:該節點的生命週期不依賴於會話,並且只有在客戶端顯示執行刪除操作的時候,他們才能被刪除
3. zookeeper單機安裝
當前測試環境centos7
jdk:jdk-8u251-linux-x64.tar.gz
zookeeper:zookeeper-3.4.9.tar.gz
- 卸載原有openjdk
rpm -qa | grep java
rpm -e --nodeps java-1.8.0-openjdk-headless-1.8.0.242.b08-1.el7.x86_64
rpm -e --nodeps python-javapackages-3.4.1-11.el7.noarch
rpm -e --nodeps java-1.8.0-openjdk-1.8.0.242.b08-1.el7.x86_64
rpm -e --nodeps javapackages-tools-3.4.1-11.el7.noarch
rpm -e --nodeps tzdata-java-2019c-1.el7.noarch
- zookeeper底層依賴於jdk,opt目錄下先進行jdk的安裝,jdk使用jdk-8u251-linux-x64.tar.gz版本,上傳並解壓jdk
tar -zxvf jdk-8u251-linux-x64.tar.gz
- 配置jdk環境變量
// 打開profile
vim /etc/profile
// 文件中加入如下內容
JAVA_HOME=/opt/jdk1.8.0_251
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH
// 使環境生效
source /etc/profile
- 檢測jdk安裝
// 敲如下命令,系統如圖反饋說明安裝成功
java -version
- zookeeper使用zookeeper-3.4.9.tar.gz,上傳並解壓
tar -zxvf zookeeper-3.4.9.tar.gz
- 爲zookeeper準備配置文件
// 配置文件
cd /opt/zookeeper-3.4.9/conf/
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
//修改一下內容,此路徑用於存儲zookeeper中數據的內存快照、及事務日誌文件
dataDir=/opt/zookeeper-3.4.9/data
創建data文件夾
cd /opt/zookeeper-3.4.9/
mkdir data
- 啓動zookeeper
// 進入zookeeper的bin目錄
cd /opt/zookeeper-3.4.9/bin/
// 啓動zookeeper
./zkServer.sh start
// 啓動:zkServer.sh start
// 停止:zkServer.sh stop
// 查看狀態:zkServer.sh status
4. zookeeper常用Shell命令
4.1 新增節點
create [-s] [-e] path data # 其中-s 爲有序節點,-e 臨時節點
創建持久化節點並寫入數據:
create /hadoop "123456"
創建持久化有序節點,此時創建的節點名爲指定節點名 + 自增序號
[zk: localhost:2181(CONNECTED) 0] create -s /a "aaa"
Created /a0000000000
[zk: localhost:2181(CONNECTED) 1] create -s /b "bbb"
Created /b0000000001
[zk: localhost:2181(CONNECTED) 2] create -s /c "ccc"
Created /c0000000002
創建臨時節點,臨時會在會話過期後被刪除:
[zk: localhost:2181(CONNECTED) 3] create -e /tmp "tmp"
Created /tmp
創建臨時有序節點,臨時會在會話過期後被刪除:
[zk: localhost:2181(CONNECTED) 4] create -s -e /aa 'aaa'
Created /aa0000000004
[zk: localhost:2181(CONNECTED) 5] create -s -e /bb 'bbb'
Created /bb0000000005
[zk: localhost:2181(CONNECTED) 6] create -s -e /cc 'ccc'
Created /cc0000000006
4.2 更新節點
更新節點的命令是 set
,可以直接進行修改,如下:
[zk: localhost:2181(CONNECTED) 9] set /hadoop "345"
cZxid = 0xa
ctime = Tue Jun 02 09:53:13 CST 2020
mZxid = 0xb
mtime = Tue Jun 02 09:53:20 CST 2020
pZxid = 0xa
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
也可以基於版本號進行修改,此時類似於樂觀鎖機制,當你傳入的數據版本號(dataVersion)和當前節點的數據版本號不符合時,zookeeper會拒絕本次修改:
[zk: localhost:2181(CONNECTED) 12] set /hadoop "159" 2
version No is not valid : /hadoop
4.3 刪除節點
刪除節點的語法如下:
delete path [version]
和更新節點數據一樣,也可以傳入版本號,當你傳入的數據版本號(dataVersion)和當前節點的數據版本號不符合時,zookeeper會拒絕本次刪除操作:
[zk: localhost:2181(CONNECTED) 13] delete /hadoop 0
version No is not valid : /hadoop
[zk: localhost:2181(CONNECTED) 14] delete /hadoop 1
[zk: localhost:2181(CONNECTED) 15]
要想刪除某個節點及其所有後代節點,可以使用遞歸刪除,命令爲 rmr path
。
4.4 查看節點
[zk: localhost:2181(CONNECTED) 17] get /hadoop
123456
cZxid = 0x11
ctime = Tue Jun 02 09:59:49 CST 2020
mZxid = 0x11
mtime = Tue Jun 02 09:59:49 CST 2020
pZxid = 0x11
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
節點各個屬性如下表。其中一個重要的概念是 Zxid(ZooKeeper Transaction
Id),ZooKeeper 節點的每一次更改都具有唯一的 Zxid,如果 Zxid1 小於 Zxid2,則
Zxid1 的更改發生在 Zxid2 更改之前。
狀態屬性 | 說明 |
---|---|
cZxid | 數據節點創建時的事務 ID |
ctime | 數據節點創建時的時間 |
mZxid | 數據節點最後一次更新時的事務 ID |
mtime | 數據節點最後一次更新時的時間 |
pZxid | 數據節點的子節點最後一次被修改時的事務 ID |
cversion | 子節點的更改次數 |
dataVersion | 節點數據的更改次數 |
aclVersion | 節點的 ACL 的更改次數 |
ephemeralOwner | 如果節點是臨時節點,則表示創建該節點的會話的SessionID;如果節點是持久節點,則該屬性值爲 0 |
dataLength | 數據內容的長度 |
numChildren | 數據節點當前的子節點個數 |
4.5 查看節點狀態
可以使用 stat
命令查看節點狀態,它的返回值和 get
命令類似,但不會返回節點數據
[zk: localhost:2181(CONNECTED) 9] stat /hadoop
cZxid = 0x18
ctime = Tue Jun 02 10:43:30 CST 2020
mZxid = 0x18
mtime = Tue Jun 02 10:43:30 CST 2020
pZxid = 0x18
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
4.6 查看節點列表
查看節點列表有 ls path
和 ls2 path
兩個命令,後者是前者的增強,不僅可以查看指定路徑下的所有節點,還可以查看當前節點的信息
[zk: localhost:2181(CONNECTED) 13] ls /hadoop
[node2, node1]
[zk: localhost:2181(CONNECTED) 14] ls2 /hadoop
[node2, node1]
cZxid = 0x18
ctime = Tue Jun 02 10:43:30 CST 2020
mZxid = 0x18
mtime = Tue Jun 02 10:43:30 CST 2020
pZxid = 0x1a
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 2
4.7 監聽器get path [watch]
使用 get path [watch]
註冊的監聽器能夠在節點內容發生改變的時候,向客戶端發出通知。需要注意的是 zookeeper 的觸發器是一次性的 (One-time trigger),即觸發一次後就會立即失效。
[zk: localhost:2181(CONNECTED) 16] get /hadoop watch
[zk: localhost:2181(CONNECTED) 17] set /hadoop "456"
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop
4.8 監聽器stat path [watch]
使用 stat path [watch]
註冊的監聽器能夠在節點狀態發生改變的時候,向客戶端發出通知
[zk: localhost:2181(CONNECTED) 18] stat /hadoop watch
[zk: localhost:2181(CONNECTED) 19] set /hadoop 1122
WATCHER::cZxid = 0x18
WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop
4.9 監聽器ls\ls2 path [watch]
使用 ls path [watch]
或 ls2 path [watch]
註冊的監聽器能夠監聽該節點下所有子節點的增加和刪除操作。
[zk: localhost:2181(CONNECTED) 20] ls /hadoop watch
[node2, node1]
[zk: localhost:2181(CONNECTED) 21] create /hadoop/yran "aaa"
WATCHER::Created /hadoop/yran
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/hadoop
5. zookeeper的acl權限控制
5.1 概述
zookeeper 類似文件系統,client 可以創建節點、更新節點、刪除節點,那麼如何做到節點的權限的控制呢?zookeeper的access control list 訪問控制列表可以做到這一點。
acl 權限控制,使用scheme:id:permission 來標識,主要涵蓋 3 個方面:
- 權限模式(scheme):授權的策略
- 授權對象(id):授權的對象
- 權限(permission):授予的權限
其特性如下:
- zooKeeper的權限控制是基於每個znode節點的,需要對每個節點設置權限
- 每個znode支持設置多種權限控制方案和多個權限
- 子節點不會繼承父節點的權限,客戶端無權訪問某節點,但可能可以訪問它的子節點
例如:
// 將節點權限設置爲IP:192.168.188.133的客戶端可以對節點進行增刪改查管理權限
[zk: localhost:2181(CONNECTED) 28] setAcl /test2 ip:192.168.188.133:crwda
5.2 權限模式
採用何種方式授權
方案 | 描述 |
---|---|
world | 只有一個用戶:anyone,代表登錄zokeeper所有人(默認) |
ip | 對客戶端使用IP地址認證 |
auth | 使用已添加認證的用戶認證 |
digest | 使用“用戶名:密碼”方式認證 |
5.3 授權的對象
給誰授予權限
授權對象ID是指,權限賦予的實體,例如:IP 地址或用戶。
5.4 授予的權限
授予什麼權限
create、delete、read、writer、admin也就是 增、刪、改、查、管理權限,這5種權限簡寫爲cdrwa,注意:這5種權限中,delete是指對子節點的刪除權限,其它4種權限指對自身節點的操作權限
權限 | ACL簡寫 | 描述 |
---|---|---|
create | c | 可以創建子節點 |
delete | d | 可以刪除子節點(僅下一級節點) |
read | r | 可以讀取節點數據及顯示子節點列表 |
write | w | 可以設置節點數據 |
admin | a | 可以設置節點訪問控制列表權限 |
5.5 授權的相關命令
命令 | 使用方式 | 描述 |
---|---|---|
getAcl | getAcl | 讀取ACL權限 |
setAcl | setAcl | 設置ACL權限 |
addauth | addauth | 添加認證用戶 |
5.6 案例
- world授權模式:
命令
setAcl <path> world:anyone:<acl>
案例
[zk: localhost:2181(CONNECTED) 1] create /node1 "node1"
Created /node1
[zk: localhost:2181(CONNECTED) 2] getAcl /node1
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 3] setAcl /node1 world:anyone:cdrwa
cZxid = 0x22
ctime = Tue Jun 02 13:29:10 CST 2020
mZxid = 0x22
mtime = Tue Jun 02 13:29:10 CST 2020
pZxid = 0x22
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
- IP授權模式
命令
setAcl <path> ip:<ip>:<acl>
案例
注意:遠程登錄zookeeper命令:./zkCli.sh -server ip
[zk: localhost:2181(CONNECTED) 4] create /node2 "node2"
Created /node2
[zk: localhost:2181(CONNECTED) 7] setAcl /node2 ip:192.168.188.131:cdrwa
cZxid = 0x24
ctime = Tue Jun 02 13:31:55 CST 2020
mZxid = 0x24
mtime = Tue Jun 02 13:31:55 CST 2020
pZxid = 0x24
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 8] getAcl /node2
'ip,'192.168.188.131
: cdrwa
- Auth授權模式:
命令
addauth digest <user>:<password> #添加認證用戶
setAcl <path> auth:<user>:<acl>
案例
[zk: localhost:2181(CONNECTED) 9] create /node3 "node3"
Created /node3
[zk: localhost:2181(CONNECTED) 10] addauth digest itcast:123456
[zk: localhost:2181(CONNECTED) 11] setAcl /node3 auth:itcast:cdrwa
cZxid = 0x26
ctime = Tue Jun 02 13:34:36 CST 2020
mZxid = 0x26
mtime = Tue Jun 02 13:34:36 CST 2020
pZxid = 0x26
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 12] getAcl /node3
'digest,'itcast:673OfZhUE8JEFMcu0l64qI8e5ek=
: cdrwa
[zk: localhost:2181(CONNECTED) 13] get /node3
node3
cZxid = 0x26
ctime = Tue Jun 02 13:34:36 CST 2020
mZxid = 0x26
mtime = Tue Jun 02 13:34:36 CST 2020
pZxid = 0x26
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
- Digest授權模式:
命令
setAcl <path> digest:<user>:<password>:<acl>
這裏的密碼是經過SHA1及BASE64處理的密文,在SHELL中可以通過以下命令計算:
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
先來計算一個密文
echo -n sky:123456 | openssl dgst -binary -sha1 | openssl base64
案例:
[zk: localhost:2181(CONNECTED) 23] create /node5 "node5"
Created /node5
[zk: localhost:2181(CONNECTED) 24] setAcl /node5 digest:sky:mjsqJQ8e6gZDKVF+t9LAARPOBjc=:cdrwa
cZxid = 0x2b
ctime = Tue Jun 02 13:42:59 CST 2020
mZxid = 0x2b
mtime = Tue Jun 02 13:42:59 CST 2020
pZxid = 0x2b
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 25] getAcl /node5
'digest,'sky:mjsqJQ8e6gZDKVF+t9LAARPOBjc=
: cdrwa
[zk: localhost:2181(CONNECTED) 26] get /node5
Authentication is not valid : /node5
[zk: localhost:2181(CONNECTED) 27] addauth digest sky:123456
[zk: localhost:2181(CONNECTED) 28] get /node5
node5
cZxid = 0x2b
ctime = Tue Jun 02 13:42:59 CST 2020
mZxid = 0x2b
mtime = Tue Jun 02 13:42:59 CST 2020
pZxid = 0x2b
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
- 多種模式授權:
同一個節點可以同時使用多種模式授權
[zk: localhost:2181(CONNECTED) 48] create /node7 "node7"
Created /node7
[zk: localhost:2181(CONNECTED) 49] addauth digest sky:123456
[zk: localhost:2181(CONNECTED) 50] setAcl /node7 ip:192.168.188.133:cdra,auth:itcast:cdrwa,digest:sky:mjsqJQ8e6gZDKVF+t9LAARPOBjc=:cdrwa
cZxid = 0x34
ctime = Tue Jun 02 13:51:50 CST 2020
mZxid = 0x34
mtime = Tue Jun 02 13:51:50 CST 2020
pZxid = 0x34
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
5.7 acl 超級管理員
zookeeper的權限管理模式有一種叫做super,該模式提供一個超管可以方便的訪問任何權限的節點
假設這個超管是:super:admin,需要先爲超管生成密碼的密文
echo -n super:admin | openssl dgst -binary -sha1 | openssl base64
那麼打開zookeeper目錄下的/bin/zkServer.sh服務器腳本文件,找到如下一行:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}"
"-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"
這就是腳本中啓動zookeeper的命令,我們需要加一個
超管的配置項
"-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="
那麼修改以後這條完整命令變成了
之後啓動zookeeper,輸入如下命令添加權限
addauth digest super:admin #添加認證用戶
6. zookeeper javaAPI
znode是zooKeeper集合的核心組件,zookeeper API提供了一小組方法使用zookeeper集合來操縱znode的所有細節。
客戶端應該遵循以步驟,與zookeeper服務器進行清晰和乾淨的交互。
- 連接到zookeeper服務器。zookeeper服務器爲客戶端分配會話ID。
- 定期向服務器發送心跳。否則,zookeeper服務器將過期會話ID,客戶端需要重新連
接。 - 只要會話ID處於活動狀態,就可以獲取/設置znode。
- 所有任務完成後,斷開與zookeeper服務器的連接。如果客戶端長時間不活動,則
zookeeper服務器將自動斷開客戶端。
6.1 連接到ZooKeeper
ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)
- connectionString - zookeeper主機
- sessionTimeout - 會話超時(以毫秒爲單位)
- watcher - 實現“監視器”對象。zookeeper集合通過監視器對象返回連接狀態。
案例:
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
public class ZookeeperConnection {
public static void main(String[] args) {
try {
// 計數器對象
CountDownLatch countDownLatch=new CountDownLatch(1);
// arg1:服務器的ip和端口
// arg2:客戶端與服務器之間的會話超時時間 以毫秒爲單位的
// arg3:監視器對象
ZooKeeper zooKeeper=new ZooKeeper("192.168.188.133:2181",
5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState()==Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 主線程阻塞等待連接對象的創建成功
countDownLatch.await();
// 會話編號
System.out.println(zooKeeper.getSessionId());
zooKeeper.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
6.2 新增節點
// 同步方式
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
// 異步方式
create(String path, byte[] data, List<ACL> acl, CreateMode createMode,
AsyncCallback.StringCallback callBack,Object ctx)
- path - znode路徑。例如,/node1 /node1/node11
- data - 要存儲在指定znode路徑中的數據
- acl - 要創建的節點的訪問控制列表。zookeeper API提供了一個靜態接口
- ZooDefs.Ids 來獲取一些基本的acl列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE返回打開znode的acl列表。
- createMode - 節點的類型,這是一個枚舉。
- callBack-異步回調接口
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ZKCreate {
private String IP = "192.168.188.133:2181";
private ZooKeeper zooKeeper;
@Before
public void before()throws Exception{
// 計數器對象
CountDownLatch countDownLatch=new CountDownLatch(1);
// arg1:服務器的ip和端口
// arg2:客戶端與服務器之間的會話超時時間 以毫秒爲單位的
// arg3:監視器對象
zooKeeper=new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState()==Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 主線程阻塞等待連接對象的創建成功
countDownLatch.await();
}
@After
public void after()throws Exception{
zooKeeper.close();
}
@Test
public void create1()throws Exception{
// arg1:節點的路徑
// arg2:節點的數據
// arg3:權限列表 world:anyone:cdrwa
// arg4:節點類型 持久化節點
zooKeeper.create("/create/node1","node1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
public void create2() throws Exception {
// Ids.READ_ACL_UNSAFE world:anyone:r
zooKeeper.create("/create/node2", "node2".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
public void create3() throws Exception {
// world授權模式
// 權限列表
List<ACL> acls = new ArrayList<ACL>();
// 授權模式和授權對象
Id id = new Id("world", "anyone");
// 權限設置
acls.add(new ACL(ZooDefs.Perms.READ, id));
acls.add(new ACL(ZooDefs.Perms.WRITE, id));
zooKeeper.create("/create/node3", "node3".getBytes(), acls, CreateMode.PERSISTENT);
}
@Test
public void create4() throws Exception {
// ip授權模式
// 權限列表
List<ACL> acls = new ArrayList<ACL>();
// 授權模式和授權對象
Id id = new Id("ip", "192.168.188.133");
// 權限設置
acls.add(new ACL(ZooDefs.Perms.ALL, id));
zooKeeper.create("/create/node4", "node4".getBytes(), acls, CreateMode.PERSISTENT);
}
@Test
public void create5() throws Exception {
// auth授權模式
// 添加授權用戶
zooKeeper.addAuthInfo("digest", "itcast:123456".getBytes());
zooKeeper.create("/create/node5", "node5".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
}
@Test
public void create6() throws Exception {
// auth授權模式
// 添加授權用戶
zooKeeper.addAuthInfo("digest", "itcast:123456".getBytes());
// 權限列表
List<ACL> acls = new ArrayList<ACL>();
// 授權模式和授權對象
Id id = new Id("auth", "itcast");
// 權限設置
acls.add(new ACL(ZooDefs.Perms.READ, id));
zooKeeper.create("/create/node6", "node6".getBytes(), acls, CreateMode.PERSISTENT);
}
@Test
public void create7() throws Exception {
// digest授權模式
// 權限列表
List<ACL> acls = new ArrayList<ACL>();
// 授權模式和授權對象
Id id = new Id("digest", "sky:mjsqJQ8e6gZDKVF+t9LAARPOBjc=");
// 權限設置
acls.add(new ACL(ZooDefs.Perms.ALL, id));
zooKeeper.create("/create/node7", "node7".getBytes(), acls, CreateMode.PERSISTENT);
}
@Test
public void create8() throws Exception {
// 持久化順序節點
// Ids.OPEN_ACL_UNSAFE world:anyone:cdrwa
String result = zooKeeper.create("/create/node8", "node8".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(result);
}
@Test
public void create9() throws Exception {
// 臨時節點
// Ids.OPEN_ACL_UNSAFE world:anyone:cdrwa
String result = zooKeeper.create("/create/node9", "node9".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println(result);
}
@Test
public void create10() throws Exception {
// 臨時順序節點
// Ids.OPEN_ACL_UNSAFE world:anyone:cdrwa
String result = zooKeeper.create("/create/node10", "node10".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(result);
}
@Test
public void create11() throws Exception {
// 異步方式創建節點
zooKeeper.create("/create/node11", "node11".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
// 0 代表創建成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 節點的路徑
System.out.println(name);
// 上下文參數
System.out.println(ctx);
}
}, "I am context");
Thread.sleep(10000);
System.out.println("結束");
}
}
6.3 更新節點
// 同步方式
setData(String path, byte[] data, int version)
// 異步方式
setData(String path, byte[] data, int version,AsyncCallback.StatCallback callBack, Object ctx)
- path- znode路徑
- data - 要存儲在指定znode路徑中的數據。
- version- znode的當前版本。每當數據更改時,ZooKeeper會更新znode的版本號。
- callBack-異步回調接口
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
public class ZKSet {
String IP = "192.168.188.133:2181";
ZooKeeper zooKeeper;
@Before
public void before() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
// arg1:zookeeper服務器的ip地址和端口號
// arg2:連接的超時時間 以毫秒爲單位
// arg3:監聽器對象
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 使主線程阻塞等待
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void set1() throws Exception {
// arg1:節點的路徑
// arg2:修改的數據
// arg3:數據版本號 -1代表版本號不參與更新
Stat stat = zooKeeper.setData("/set/node1", "node13".getBytes(), -1);
// 當前節點的版本號
System.out.println(stat.getVersion());
}
@Test
public void set2() throws Exception {
zooKeeper.setData("/set/node1", "node14".getBytes(), -1, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
// 0代表修改成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 上下文參數對象
System.out.println(ctx);
// 屬性描述對象
System.out.println(stat.getVersion());
}
}, "I am Context");
Thread.sleep(10000);
System.out.println("結束");
}
}
6.4 刪除節點
// 同步方式
delete(String path, int version)
// 異步方式
delete(String path, int version, AsyncCallback.VoidCallback callBack,
Object ctx)
- path - znode路徑。
- version - znode的當前版本
- callBack-異步回調接口
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
public class ZKDelete {
String IP = "192.168.188.133:2181";
ZooKeeper zooKeeper;
@Before
public void before() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
// arg1:zookeeper服務器的ip地址和端口號
// arg2:連接的超時時間 以毫秒爲單位
// arg3:監聽器對象
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 使主線程阻塞等待
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void delete1() throws Exception {
// arg1:刪除節點的節點路徑
// arg2:數據版本信息 -1代表刪除節點時不考慮版本信息
zooKeeper.delete("/delete/node1",-1);
}
@Test
public void delete2() throws Exception {
// 異步使用方式
zooKeeper.delete("/delete/node2", -1, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
// 0代表刪除成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 上下文參數對象
System.out.println(ctx);
}
},"I am Context");
Thread.sleep(10000);
System.out.println("結束");
}
}
6.5 查看節點
// 同步方式
getData(String path, boolean b, Stat stat)
// 異步方式
getData(String path, boolean b,AsyncCallback.DataCallback callBack,
Object ctx)
- path - znode路徑。
- b- 是否使用連接對象中註冊的監視器。
- stat - 返回znode的元數據。
- callBack-異步回調接口
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
public class ZKGet {
String IP = "192.168.188.133:2181";
ZooKeeper zooKeeper;
@Before
public void before() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
// arg1:zookeeper服務器的ip地址和端口號
// arg2:連接的超時時間 以毫秒爲單位
// arg3:監聽器對象
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 使主線程阻塞等待
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void get1() throws Exception {
// arg1:節點的路徑
// arg3:讀取節點屬性的對象
Stat stat=new Stat();
byte [] bys=zooKeeper.getData("/get/node1",false,stat);
// 打印數據
System.out.println(new String(bys));
// 版本信息
System.out.println(stat.getVersion());
}
@Test
public void get2() throws Exception {
//異步方式
zooKeeper.getData("/get/node1", false, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
// 0代表讀取成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 上下文參數對象
System.out.println(ctx);
// 數據
System.out.println(new String(data));
// 屬性對象
System.out.println(stat.getVersion());
}
},"I am Context");
Thread.sleep(10000);
System.out.println("結束");
}
}
6.6 查看子節點
// 同步方式
getChildren(String path, boolean b)
// 異步方式
getChildren(String path, boolean b,AsyncCallback.ChildrenCallback
callBack,Object ctx)
- path - Znode路徑。
- b- 是否使用連接對象中註冊的監視器。
- callBack - 異步回調接口。
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ZKGetChid {
String IP = "192.168.188.133:2181";
ZooKeeper zooKeeper;
@Before
public void before() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
// arg1:zookeeper服務器的ip地址和端口號
// arg2:連接的超時時間 以毫秒爲單位
// arg3:監聽器對象
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 使主線程阻塞等待
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void get1() throws Exception {
// arg1:節點的路徑
List<String> list = zooKeeper.getChildren("/get", false);
for (String str : list) {
System.out.println(str);
}
}
@Test
public void get2() throws Exception {
// 異步用法
zooKeeper.getChildren("/get", false, new AsyncCallback.ChildrenCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List<String> children) {
// 0代表讀取成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 上下文參數對象
System.out.println(ctx);
// 子節點信息
for (String str : children) {
System.out.println(str);
}
}
},"I am Context");
Thread.sleep(10000);
System.out.println("結束");
}
}
6.7 檢查節點是否存在
// 同步方法
exists(String path, boolean b)
// 異步方法
exists(String path, boolean b,AsyncCallback.StatCallback callBack,Object
ctx)
- path- znode路徑。
- b- 是否使用連接對象中註冊的監視器。
- callBack - 異步回調接口。
- ctx-傳遞上下文參數
案例:
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
public class ZKExists {
String IP = "192.168.188.133:2181";
ZooKeeper zooKeeper;
@Before
public void before() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
// arg1:zookeeper服務器的ip地址和端口號
// arg2:連接的超時時間 以毫秒爲單位
// arg3:監聽器對象
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("連接創建成功!");
countDownLatch.countDown();
}
}
});
// 使主線程阻塞等待
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void exists1() throws Exception {
// arg1:節點的路徑
Stat stat=zooKeeper.exists("/exists1",false);
// 節點的版本信息
System.out.println(stat.getVersion());
}
@Test
public void exists2() throws Exception {
// 異步方式
zooKeeper.exists("/exists1", false, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
// 0 代表方式執行成功
System.out.println(rc);
// 節點的路徑
System.out.println(path);
// 上下文參數
System.out.println(ctx);
// 節點的版本信息
System.out.println(stat.getVersion());
}
},"I am Context");
Thread.sleep(10000);
System.out.println("結束");
}
}