zk的基礎知識基本分為三大模塊
- 數據模型
- ACL 權限控制
- Watch 監控
數據模型
默認配置文件
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper
- tickTime client-server 通信心跳時間
- zk 服務器之間或client 與服務器之間維持心跳的時間間隔、也就是每個tickTime 就會發送一個心跳、tickTime 以毫秒為單位
- initLimit leader-follower 初始通信時限
- 集群中 follower 與leader 之間初始連接時最多能容忍的最多心跳數 。
- syncLimit leader follower 同步通信時限
- follower與 leader 服務器請求與應答之間能容忍的最多心跳數
- dataDir 數據目錄
啟動zk
bin/zkServer.sh start
使用客戶端連接
bin/zkCli.sh -server 127.0.0.1:2181
create /locks ""
create /servers ""
create /works ""
成功創建之后,命令窗口顯示
Created /servers


1. 不支持遞歸創建,必須先創建父節點
2. 節點不能以 / 結尾,會直接報錯
3. ZooKeeper 樹中的每一層級用斜杠(/)分隔開, 且只能用絕對路徑(如“get /work/task1”)的方式查詢 ZooKeeper 節點, 而不能使用相對路徑
數據模型是一種樹形結構 。
znode 節點類型與特性
zk中的數據節點也分為持久節點、臨時節點和順序節點 。
持久節點
持久節點一旦創建、該數據節點會一直存儲在zk服務器上、即使創建該節點的客戶端與服務端的會話關閉了、該節點也不會被刪除
臨時節點
如果將節點創建為臨時節點、那么該節點數據不會一只存儲在zk服務器上、當創建該臨時節點的客戶端繪畫因超時或發生異常而關閉時、該節點也相應的在zk上被刪除 。
有序節點
有序節點並不是一種單獨種類的節點、而是在吃酒節點和臨時節點的基礎上、增加了一個節點有序的性質 。
create -s /sequence-node- ""
Created /sequence-node-0000000017
第二次
create -s /sequence-node- ""
Created /sequence-node-0000000018
.......
.......
上述幾種數據節點雖然類型不同、但zk中每個節點都維護有這些內容:一個二進制數組(用來存儲節點數據)、ACL訪問控制信息、子節點數據(因為臨時節點不允許有子節點、所以其子節點字段為null)、自身狀態信息字段stat 。
節點的狀態結構
get /servers stat /servers
返回的結果
cZxid = 0x4f0
ctime = Thu May 07 21:53:04 CST 2020
mZxid = 0x4f0
mtime = Thu May 07 21:53:04 CST 2020
pZxid = 0x4f0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0
- cZxid 創建這個節點的事務id
- ctime 創建這個節點的時間
- mZxid 修改這個節點的事務id
- mtime 修改這個節點的時間
- pZxid 表示該節點的子節點列表最后一次被修改時的事務id
- cversion 這表示對此znode的子節點進行的更改次數
- dataVersion 表示對該znode的數據所做的更改次數
- aclVersion 表示對此znode的ACL進行更改的次數
- ephemeralOwner 如果該節點是臨時節點、那么此字段記錄的是znode所有者的sessionId、如果不是則為0
- dataLength znode 數據字段的長度 .
- numChildre znode 子節點的數量
使用zookeeper實現鎖
針對超賣問題、使用zk實現鎖
悲觀鎖
認為對臨界區的競爭總是會出現、為了保證在操作數據時、該數據不被其他進程修改、數據一直會處於鎖定的狀態 。
我們假設一個具有 n 個進程的應用,同時訪問臨界區資源,我們通過進程創建 ZooKeeper 節點 /locks 的方式獲取鎖。
線程 a 通過成功創建 ZooKeeper 節點“/locks”的方式獲取鎖后繼續執行,如下圖所示:

線程 a 通過成功創建 ZooKeeper 節點“/locks”的方式獲取鎖后繼續執行,如下圖所示:

這樣就實現了一個簡單的悲觀鎖,不過這也有一個隱含的問題,就是當進程 a 因為異常中斷導致 /locks 節點始終存在,其他線程因為無法再次創建節點而無法獲取鎖,這就產生了一個死鎖問題。針對這種情況我們可以通過將節點設置為臨時節點的方式避免。並通過在服務器端添加監聽事件來通知其他進程重新獲取鎖。
樂觀鎖
進程對臨界區資源的競爭不會總是出現、所以相對悲觀鎖而已、假鎖方式沒那么激烈、不會全程鎖定資源、而是在數據進行提交更新的時候、對數據的沖突與否進行檢查、如果發現沖突了、則拒絕操作
樂觀鎖基本都是CAS、CAS有三個操作數、內存值V、舊的預期值A、修改的新值B、當預期值與內存值V 相等時、才將內存值V改成B
在zk中的version 屬性就是用來實現樂觀鎖機制中的檢驗的、zk中每個節點都有 dataVersion 這個字段、在調用更新操作的時候、加入有一個客戶端試圖進行更新操作、他會攜帶上次獲取到的version值進行更新、如果在這段時間內、zk服務器上該節點的數值恰好已經被其他客戶端更新了、那么 dataVersion一定發生變化、那么與當前客戶端的version 無法匹配、便無法更新
在 ZooKeeper 的底層實現中,當服務端處理 setDataRequest 請求時,首先會調用 checkAndIncVersion 方法進行數據版本校驗。ZooKeeper 會從 setDataRequest 請求中獲取當前請求的版本 version,同時通過 getRecordForPath 方法獲取服務器數據記錄 nodeRecord, 從中得到當前服務器上的版本信息 currentversion。如果 version 為 -1,表示該請求操作不使用樂觀鎖,可以忽略版本對比;如果 version 不是 -1,那么就對比 version 和 currentversion,如果相等,則進行更新操作,否則就會拋出 BadVersionException 異常中斷操作。
思考問題
為啥zk不采用相對路徑來查找結點?
這是因為 ZooKeeper 大多是應用場景是定位數據模型上的節點,並在相關節點上進行操作。像這種查找與給定值相等的記錄問題最適合用散列來解決。因此 ZooKeeper 在底層實現的時候,使用了一個 hashtable,即 hashtableConcurrentHashMap<String, DataNode> nodes ,用節點的完整路徑來作為 key 存儲節點數據。這樣就大大提高了 ZooKeeper 的性能。
新版本
zookeeper 3.5.x 中引入了 container 節點 和 ttl 節點(不穩定)
- container 節點用來存放子節點,如果container節點中的子節點為0 ,則container節點在未來會被服務器刪除。
- ttl 節點默認禁用,需要通過配置開啟, 如果ttl 節點沒有子節點,或者 ttl 節點在 指定的時間內沒有被修改則會被服務器刪除。
https://www.cnblogs.com/skyl/p/4854553.html
https://www.cnblogs.com/IcanFixIt/p/7846361.html
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=158#/detail/pc?id=3131

