【Zookeekper】分佈鎖Curator

  1. 有序節點:假如當前有一個父節點爲/lock,咱們能夠在這個父節點下面建立子節點;zookeeper提供了一個可選的有序特性,例如咱們能夠建立子節點「/lock/node-」而且指明有序,那麼zookeeper在生成子節點時會根據當前的子節點數量自動添加整數序號,也就是說若是是第一個建立的子節點,那麼生成的子節點爲/lock/node-0000000000,下一個節點則爲/lock/node-0000000001,依次類推。
  2. 臨時節點:客戶端能夠創建一個臨時節點,在會話結束或者會話超時後,zookeeper會自動刪除該節點。
  3. 事件監聽:在讀取數據時,咱們能夠同時對節點設置事件監聽,當節點數據或結構變化時,zookeeper會通知客戶端。當前zookeeper有以下四種事件:1)節點建立;2)節點刪除;3)節點數據修改;4)子節點變動。

下面描述使用zookeeper實現分佈式鎖的算法流程,假設鎖空間的根節點爲/lock:html

  1. 客戶端鏈接zookeeper,並在/lock下建立臨時的有序的子節點,第一個客戶端對應的子節點爲/lock/lock-0000000000,第二個爲/lock/lock-0000000001,以此類推。
  2. 客戶端獲取/lock下的子節點列表,判斷本身建立的子節點是否爲當前子節點列表中序號最小的子節點,若是是則認爲得到鎖,不然監聽/lock的子節點變動消息,得到子節點變動通知後重復此步驟直至得到鎖;
  3. 執行業務代碼;
  4. 完成業務流程後,刪除對應的子節點釋放鎖。

步驟1中建立的臨時節點可以保證在故障的狀況下鎖也能被釋放,考慮這麼個場景:假如客戶端a當前建立的子節點爲序號最小的節點,得到鎖以後客戶端所在機器宕機了,客戶端沒有主動刪除子節點;若是建立的是永久的節點,那麼這個鎖永遠不會釋放,致使死鎖;因爲建立的是臨時節點,客戶端宕機後,過了必定時間zookeeper沒有收到客戶端的心跳包判斷會話失效,將臨時節點刪除從而釋放鎖。java

另外細心的朋友可能會想到,在步驟2中獲取子節點列表與設置監聽這兩步操做的原子性問題,考慮這麼個場景:客戶端a對應子節點爲/lock/lock-0000000000,客戶端b對應子節點爲/lock/lock-0000000001,客戶端b獲取子節點列表時發現本身不是序號最小的,可是在設置監聽器前客戶端a完成業務流程刪除了子節點/lock/lock-0000000000,客戶端b設置的監聽器豈不是丟失了這個事件從而致使永遠等待了?這個問題不存在的。由於zookeeper提供的API中設置監聽器的操做與讀操做是原子執行的,也就是說在讀子節點列表時同時設置監聽器,保證不會丟失事件。node

最後,對於這個算法有個極大的優化點:假如當前有1000個節點在等待鎖,若是得到鎖的客戶端釋放鎖時,這1000個客戶端都會被喚醒,這種狀況稱爲「羊羣效應」;在這種羊羣效應中,zookeeper須要通知1000個客戶端,這會阻塞其餘的操做,最好的狀況應該只喚醒新的最小節點對應的客戶端。應該怎麼作呢?在設置事件監聽時,每一個客戶端應該對恰好在它以前的子節點設置事件監聽,例如子節點列表爲/lock/lock-0000000000、/lock/lock-000000000一、/lock/lock-0000000002,序號爲1的客戶端監聽序號爲0的子節點刪除消息,序號爲2的監聽序號爲1的子節點刪除消息。算法

因此調整後的分佈式鎖算法流程以下:apache

  1. 客戶端鏈接zookeeper,並在/lock下建立臨時的有序的子節點,第一個客戶端對應的子節點爲/lock/lock-0000000000,第二個爲/lock/lock-0000000001,以此類推。
  2. 客戶端獲取/lock下的子節點列表,判斷本身建立的子節點是否爲當前子節點列表中序號最小的子節點,若是是則認爲得到鎖,不然監聽恰好在本身以前一位的子節點刪除消息,得到子節點變動通知後重復此步驟直至得到鎖;
  3. 執行業務代碼;
  4. 完成業務流程後,刪除對應的子節點釋放鎖。

Curator實現

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.0</version>
</dependency>
public static void main(String[] args) throws Exception {
    //建立zookeeper的客戶端
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy);
    client.start();

    //建立分佈式鎖, 鎖空間的根節點路徑爲/curator/lock
    InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
    mutex.acquire();
    //得到了鎖, 進行業務流程
    System.out.println("Enter mutex");
    //完成業務流程, 釋放鎖
    mutex.release();
    
    //關閉客戶端
    client.close();
}

  

摘錄自:http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html分佈式

相關文章
相關標籤/搜索