zookeeper實現分散式鎖及 如何避免羊群效應
問題導讀: 1.zookeeper如何實現分散式鎖? 2.什麼是羊群效應? 3.zookeeper如何釋放鎖?
在zookeeper應用場景有關於分散式叢集配置檔案同步問題的描述,設想一下如果有100臺機器同時對同一臺機器上某個檔案進行修改,如何才能保證文字不會被寫亂,這就是最簡單的分散式鎖,本文介紹利用zk實現分散式鎖。下面是寫鎖的實現步驟
分散式寫鎖 create一個PERSISTENT型別的znode,/Locks/write_lock
1.客戶端建立SEQUENCE|EPHEMERAL型別的znode,名字是lockid開頭,建立的znode是/Locks/write_lock/lockid0000000001 2.呼叫getChildren()不要設定Watcher獲取/Locks/write_lock下的znode列表 3.判斷自己步驟2建立znode是不是znode列表中最小的一個,如果是就代表獲得了鎖,如果不是往下走 4.呼叫exists()判斷步驟2自己建立的節點編號小1的znode節點(也就是獲取的znode節點列表中最小的znode),並且設定Watcher,如果exists()返回false,執行步驟3 5.果exists()返回true,那麼等待zk通知,從而在回掉函式裡返回執行步驟3
如果首先介紹一下,Zookeeper中有一種節點叫做順序節點,故名思議,假如我們在/lock/目錄下建立節3個點,ZooKeeper叢集會按照提起建立的順序來建立節點,節點分別為/lock/0000000001、/lock/0000000002、/lock/0000000003。 ZooKeeper中還有一種名為臨時節點的節點,臨時節點由某個客戶端建立,當客戶端與ZooKeeper叢集斷開連線,則開節點自動被刪除。 利用上面這兩個特性,我們來看下獲取實現分散式鎖的基本邏輯: 客戶端呼叫create()方法建立名為“locknode/guid-lock-”的節點,需要注意的是,這裡節點的建立型別需要設定為EPHEMERAL_SEQUENTIAL。(臨時有序節點) 客戶端呼叫getChildren(“locknode”)方法來獲取所有已經建立的子節點,同時在這個節點上註冊上子節點變更通知的Watcher。 客戶端獲取到所有子節點path之後,如果發現自己在步驟1中建立的節點是所有節點中序號最小的,那麼就認為這個客戶端獲得了鎖。 如果在步驟3中發現自己並非是所有子節點中最小的,說明自己還沒有獲取到鎖,就開始等待,直到下次子節點變更通知的時候,再進行子節點的獲取,判斷是否獲取鎖。 釋放鎖的過程相對比較簡單,就是刪除自己建立的那個子節點即可。exists()返回true,那麼等待zk通知,從而在回掉函式裡返回執行步驟3
釋放鎖就是刪除znode節點或者斷開連線就行
*注意:上面步驟2中getChildren()不設定Watcher的原因是,防止羊群效應,如果getChildren()設定了Watcher,那麼叢集一抖動都會收到通知。在整個分散式鎖的競爭過程中,大量重複執行,並且絕大多數的執行結果都是判斷出自己並非是序號最小的節點,從而繼續等待下一次通知—,這個顯然看起來不怎麼科學。客戶端無端的接受到過多的和自己不相關的事件通知,這如果在叢集規模大的時候,會對Server造成很大的效能影響,並且如果一旦同一時間有多個節點的客戶端斷開連線,這個時候,伺服器就會像其餘客戶端傳送大量的事件通知——這就是所謂的羊群效應。 下面是程式碼實現
Watcher 觸發條件:
客戶端是通過 getData、 getChildren 和 exist 三個介面來向 ZooKeeper 伺服器註冊 Watcher,且需要多次註冊
增、刪、改 ( 重複修改也會觸發,因為他只告訴你變更了,不告訴你變更多少,需要 客戶端 自己去拿)