版權聲明:本文為博主原創文章,轉載請注明出處: http://blog.csdn.net/lihao21 或 leehao.me
在ZooKeeper中,節點也稱為znode。由於對於程序員來說,對zk的操作主要是對znode的操作,因此,有必要對znode進行深入的了解。
ZooKeeper采用了類似文件系統的的數據模型,其節點構成了一個具有層級關系的樹狀結構。例如,圖1展示了zk節點的層級樹狀結構。
圖1中,根節點 / 包含了兩個字節點 /module1,/module2,而節點 /module1 又包含了三個字節點 /module1/app1,/module1/app2,/module1/app3。在zk中,節點以絕對路徑表示,不存在相對路徑,且路徑最后不能以 / 結尾(根節點除外)。
類型
根據節點的存活時間,可以對節點划分為持久節點和臨時節點。節點的類型在創建時就被確定下來,並且不能改變。
持久節點的存活時間不依賴於客戶端會話,只有客戶端在顯式執行刪除節點操作時,節點才消失。
臨時節點的存活時間依賴於客戶端會話,當會話結束,臨時節點將會被自動刪除(當然也可以手動刪除臨時節點)。利用臨時節點的這一特性,我們可以使用臨時節點來進行集群管理,包括發現服務的上下線等。
ZooKeeper規定,臨時節點不能擁有子節點。
持久節點
使用命令create可以創建一個持久節點。
create /module1 module1
這樣,便創建了一個持久節點/module1,且其數據為”module1”。
臨時節點
使用create命令,並加上-e參數,可以創建一個臨時節點。
create -e /module1/app1 app1
這樣,便創建了一個臨時節點 /module1/app1,數據為”app1”。關閉會話,然后輸入命令:
get /module1/app1
可以看到有以下提示,說明臨時節點已經被刪除。
Node does not exist: /module1/app1
順序節點
ZooKeeper中還提供了一種順序節點的節點類型。每次創建順序節點時,zk都會在路徑后面自動添加上10位的數字(計數器),例如 < path >0000000001,< path >0000000002,……這個計數器可以保證在同一個父節點下是唯一的。在zk內部使用了4個字節的有符號整形來表示這個計數器,也就是說當計數器的大小超過2147483647時,將會發生溢出。
順序節點為節點的一種特性,也就是,持久節點和臨時節點都可以設置為順序節點。這樣一來,znode一共有4種類型:持久的、臨時的,持久順序的,臨時順序的。
使用命令create加上-s參數,可以創建順序節點,例如,
create -s /module1/app app
輸出:
Created /module1/app0000000001
便創建了一個持久順序節點 /module1/app0000000001。如果再執行此命令,則會生成節點 /module1/app0000000002。
如果在create -s再添加-e參數,則可以創建一個臨時順序節點。
節點的數據
在創建節點時,可以指定節點中存儲的數據。ZooKeeper保證讀和寫都是原子操作,且每次讀寫操作都是對數據的完整讀取或完整寫入,並不提供對數據進行部分讀取或者寫入的操作。
以下命令創建一個節點/module1/app2,且其存儲的數據為app2。
create /module1/app2 app2
ZooKeeper雖然提供了在節點存儲數據的功能,但它並不將自己定位為一個通用的數據庫,也就是說,你不應該在節點存儲過多的數據。Zk規定節點的數據大小不能超過1M,但實際上我們在znode的數據量應該盡可能小,因為數據過大會導致zk的性能明顯下降。如果確實需要存儲大量的數據,一般解決方法是在另外的分布式數據庫(例如redis)中保存這部分數據,然后在znode中我們只保留這個數據庫中保存位置的索引即可。
節點的屬性
每個znode都包含了一系列的屬性,通過命令get,我們可以獲得節點的屬性。
get /module1/app2
app2
cZxid = 0x20000000e
ctime = Thu Jun 30 20:41:55 HKT 2016
mZxid = 0x20000000e
mtime = Thu Jun 30 20:41:55 HKT 2016
pZxid = 0x20000000e
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
版本號
對於每個znode來說,均存在三個版本號:
- dataVersion
數據版本號,每次對節點進行set操作,dataVersion的值都會增加1(即使設置的是相同的數據)。 - cversion
子節點的版本號。當znode的子節點有變化時,cversion 的值就會增加1。 - aclVersion
ACL的版本號,關於znode的ACL(Access Control List,訪問控制),可以參考 參考資料1 有關ACL的描述。
以數據版本號來說明zk中版本號的作用。每一個znode都有一個數據版本號,它隨着每次數據變化而自增。ZooKeeper提供的一些API例如setData和delete根據版本號有條件地執行。多個客戶端對同一個znode進行操作時,版本號的使用就會顯得尤為重要。例如,假設客戶端C1對znode /config寫入一些配置信息,如果另一個客戶端C2同時更新了這個znode,此時C1的版本號已經過期,C1調用setData一定不會成功。這正是版本機制有效避免了數據更新時出現的先后順序問題。在這個例子中,C1在寫入數據時使用的版本號無法匹配,使得操作失敗。圖2描述了這個情況。
圖2:使用版本號來阻止並行操作的不一致性
事務ID
對於zk來說,每次的變化都會產生一個唯一的事務id,zxid(ZooKeeper Transaction Id)。通過zxid,可以確定更新操作的先后順序。例如,如果zxid1小於zxid2,說明zxid1操作先於zxid2發生。
需要指出的是,zxid對於整個zk都是唯一的,即使操作的是不同的znode。
-
cZxid
Znode創建的事務id。 -
mZxid
Znode被修改的事務id,即每次對znode的修改都會更新mZxid。
圖3:Zxid在客戶端重連中的作用
在集群模式下,客戶端有多個服務器可以連接,當嘗試連接到一個不同的服務器時,這個服務器的狀態要與最后連接的服務器的狀態要保持一致。Zk正是使用zxid來標識這個狀態,圖3描述了客戶端在重連情況下zxid的作用。當客戶端因超時與S1斷開連接后,客戶端開始嘗試連接S2,但S2延遲於客戶端所識別的狀態。然而,S3的狀態與客戶端所識別的狀態一致,所以客戶端可以安全連接上S3。
時間戳
包括znode的創建時間和修改時間,創建時間是znode創建時的時間,創建后就不會改變;修改時間在每次更新znode時都會發生變化。
以下命令創建了一個 /module2 節點。
create /module2 module2
Created /module2
通過 get 命令,可以看到 /module2的 ctime和mtime均為Sat Jul 02 11:18:32 CST 2016。
get /module2
module2
cZxid = 0x2
ctime = Sat Jul 02 11:18:32 CST 2016
mZxid = 0x2
mtime = Sat Jul 02 11:18:32 CST 2016
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
修改 /module2,可以看到 ctime 沒有發生變化,mtime已更新為最新的時間。
set /module2 module2_1
cZxid = 0x2
ctime = Sat Jul 02 11:18:32 CST 2016
mZxid = 0x3
mtime = Sat Jul 02 11:18:50 CST 2016
pZxid = 0x2
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
參考資料