1. 程式人生 > >如何用Zookeeper來實現分散式鎖?

如何用Zookeeper來實現分散式鎖?

什麼是Zookeeper臨時順序節點?
例如 :
/
動物 植物
貓 倉鼠 荷花 松樹
Zookeeper的資料儲存結構就像一棵樹,這棵樹由節點組成,這種節點叫做Zonde.
# Znode分為四種類型 :
1.持久節點(PERSISTENT)
預設的節點型別.建立節點的客戶端與zookeeper斷開連線後,該節點依舊存在.
2.持久節點順序節點(PERSISTENT_SEQUENTIAL)
所謂順序節點,就是在建立節點時,Zookeeper根據建立的時間順序給該節點名稱進行編號.
例如 :
倉鼠
倉鼠0001 倉鼠0002 倉鼠0003

3.臨時節點(EPHEMERAL)
和持久節點相反,當建立節點的客戶端與zookeeper斷開連線後,臨時節點會被刪除 :
4.臨時順序節點(EPHEMERAL_SEQUENTIAL)
臨時順序節點結合了臨時節點和順序節點的特點 : 在建立節點時,Zookeeper根據建立的時間順序給該節點名稱進行編號 ; 當建立節點的客戶端與zookeeper
斷開連線後,臨時節點會被刪除.
# Zookeeper分散式鎖的原理
Zookeeper分散式鎖恰恰應用了臨時順序節點
## 獲取鎖 :
首先,在Zookeeper當中建立一個持久節點ParentLock.當第一個客戶端想要獲得鎖時,需要在ParentLock這個節點下面建立一個臨時順序節點Lock1.
之後,Client1查詢ParentLock下面所有的臨時順序節點並排序,判斷自己所建立的節點Lock1是不是順序最靠前的一個.如果是第一個節點,則成功獲得鎖.
這時候,如果再有一個客戶端Client2前來獲取鎖,則在ParentLock下再建立一個臨時順序節點Lock2.
Client2查詢ParentLock下面所有的臨時順序節點並排序,判斷自己所建立的節點Lock2是不是順序最靠前的一個,結果發現節點Lock2並不是最小的.
於是,Client2向排序僅比它靠前的節點Lock2註冊Watcher,用於監聽Lock1節點是否存在.這意味著Client2搶鎖失敗,進入了等待狀態.
這時候,如果又有一個客戶端Client3前來獲取鎖,則在ParentLock下再建立一個臨時順序節點Lock3.
Client3查詢ParentLock下面所有的臨時順序節點並排序,判斷自己所建立的節點Lock3是不是順序最靠前的一個,結果同樣發現節點Lock3並不是最小的.
於是,Client3向排序僅比它靠前的節點Lock2註冊Watcher,用於監聽Lock2節點是否存在.這意味著Client3同樣搶鎖失敗,進入了等待狀態.
這樣一來,Client1得到了鎖,Client2監聽了Lock1,Client3監聽了Lock2.這恰恰形成了一個等待佇列,很像是Java當中的ReentrantLock所依賴的AQS(AbstractQueuedSynchronizer)
## 釋放鎖 :
釋放鎖分為兩種情況 :
1.任務完成,客戶端顯示釋放
當任務完成時,Client1會顯示呼叫刪除節點Lock1的指令.
2.任務執行過程中,客戶端崩潰
獲得鎖的Client1在任務執行過程中,如果Duang的一聲崩潰則會斷開與Zookeeper服務端的連結.根據臨時節點的特性,相關聯的節點Lock1會隨之自動刪除.
由於Client2一直監聽著Lock1的存在狀態,當Lock1節點被刪除,Client2會立刻收到通知.這時候Client2會再次查詢ParentLock下面的所有節點,確認自己建立
的節點Lock2是不是目前最小的節點.如果是最小,則Client2順利成章獲得鎖.
同理,如果Client2也因為任務完成或者節點崩潰而刪除了節點Lock2,那麼Client3就會接到通知.最終,Client3成功得到了鎖.
Zookeeper和Redis分散式鎖的比較
Zookeeper的
優點 :
1.有封裝好的框架,容易實現.
2.有等待鎖的佇列,大大提升搶鎖效率.
缺點 :
1.新增和刪除節點效能較低.
Redis
優點 : Set和Del指令的效能較高.
缺點 :
1.實現複雜,需要考慮超時,原子性,誤刪等情形.
2.沒有等待鎖的佇列,只能在客戶端自旋等鎖,效率低下.
兩者都可以在客戶端實現可重入邏輯.
在Apache的開源框架Apache Curator中,包含了對Zookeeper分散式鎖的實現,原始碼 : https://github.com/apache/cura