zookeeper分佈式鎖實現原理

    文章轉載自https://blog.csdn.net/koflance/article/details/78616206html

一、互斥鎖mutex lock

顧名思義就是排它鎖,同一時間只容許一個客戶端執行。node

實現步驟:apache

  • 首先,建立一個lock node,例如「locknode
  • 其次,客戶端lock執行如下方式: 
    1. 建立(create)一個有序臨時節點,例如「locknode/guid-lock-」,其中guid能夠是你客戶端的惟一識別序號,若是發生前面說的建立失敗問題,須要使用guid進行手動檢查。
    2. 調用getChildren(watch=false)獲取獲取子節點列表,注意wtach設置爲false,以免羊羣效應(Herd Effect),即同時收到太多無效節點刪除通知。
    3. 從這個列表中,判斷本身建立的節點序號是不是最小,若是是則直接返回true,不然繼續往下走。
    4. 從步驟2中獲取的list中選取排在當前節點前一位的節點,調用exist(watch=true)方法。
    5. 若是exist返回false,則回到步驟2;
    6. 若是exist返回true,則等待exist的哨兵(watch)回調通知,收到通知後再執行步驟2.
  • 最後,客戶端unlock只須要調用delete刪除掉節點便可。

節點操做示意圖:ui

這裏寫圖片描述

流程圖:spa

這裏寫圖片描述

優勢.net

  • 避免了輪詢和超時控制
  • 每次一個子節點的刪除動做,只會觸發惟一一個客戶端的watch動做,從而避免了羊羣效應
  • 便於觀測

缺點server

  • 沒有解決鎖重入問題,由於採用的是有序臨時節點,所以屢次調用create並不會觸發KeeperException.NodeExists異常,從而沒法實現鎖重入功能。若是須要解決,則在步驟1時,須要先進行判斷當前節點是否已經存在,即調用getChildren(watch=false),判斷當前節點是否已經建立(配合guid),已經建立,則直接從步驟3開始,沒有建立則從步驟1開始。
  • 這是一個公平鎖,沒法實現非公平鎖。參考[4]實現了一個非公平鎖

注意:htm

若是一個節點建立了一個sequential ephemeral nodes,可是在server返回建立成功的時候,server掛了,此時客戶端須要從新鏈接,從新鏈接後會話依然有效,但其建立的臨時節點卻沒有刪除。解決方式就是在每次建立時create,若是發生失敗,客戶端須要getChildren(),進行手動檢查是否獲取鎖,這個時候就須要使用guid。blog

二、共享鎖Shared Locks或讀寫鎖Read/Write Locks

Read讀鎖是共享鎖,Write寫鎖是排它鎖,當沒有寫時,容許多個read實例獲取讀鎖,當有一個write實例得到寫鎖時,則不容許任何其餘write實例和read實例得到鎖。圖片

實現步驟:

  • 首先,建立一個lock node,例如「locknode
  • 獲取read鎖步驟: 
    1. 建立(create)一個有序臨時節點,例如「locknode/read-guid-lock-」,其中guid能夠是你客戶端的惟一識別序號,若是發生前面說的建立失敗問題,須要使用guid進行手動檢查。
    2. 調用getChildren(watch=false)獲取獲取子節點列表,注意wtach設置爲false,以免羊羣效應(Herd Effect),即同時收到太多無效節點刪除通知。
    3. 從這個列表中,判斷是否有序號比本身小、且路徑名以「write-」開頭的節點,若是沒有,則直接獲取讀鎖,不然繼續以下步驟。
    4. 從步驟2中獲取的list中選取排在當前節點前一位的、且路徑名以「write-」開頭的節點,調用exist(watch=true)方法。
    5. 若是exist返回false,則回到步驟2。
    6. 若是exist返回true,則等待exist的哨兵(watch)回調通知,收到通知後再執行步驟2。
  • 獲取write鎖步驟: 
    1. 建立(create)一個有序臨時節點,例如「locknode/write-guid-lock-」,其中guid能夠是你客戶端的惟一識別序號,若是發生前面說的建立失敗問題,須要使用guid進行手動檢查。
    2. 調用getChildren(watch=false)獲取獲取子節點列表,注意wtach設置爲false,以免羊羣效應(Herd Effect),即同時收到太多無效節點刪除通知。
    3. 從這個列表中,判斷本身建立的節點序號是不是最小,若是是則直接返回true,不然繼續往下走。
    4. 從步驟2中獲取的list中選取排在當前節點前一位的節點,調用exist(watch=true)方法。
    5. 若是exist返回false,則回到步驟2;
    6. 若是exist返回true,則等待exist的哨兵(watch)回調通知,收到通知後再執行步驟2.
  • 最後,客戶端unlock只須要調用delete刪除掉節點便可。

節點操做示意圖:

這裏寫圖片描述

流程圖:

  • read lock

這裏寫圖片描述

  • write lock

這裏寫圖片描述

優勢

  • 避免了輪詢和超時控制
  • 每次一個子節點的刪除動做,只會觸發惟一一個客戶端的watch動做,從而避免了羊羣效應
  • 便於觀測

缺點

  • 沒有解決鎖重入問題,由於採用的是有序臨時節點,所以屢次調用create並不會觸發KeeperException.NodeExists異常,從而沒法實現鎖重入功能。若是須要解決,則在步驟1時,須要先進行判斷當前節點是否已經存在,即調用getChildren(watch=false),判斷當前節點是否已經建立(配合guid),已經建立,則直接從步驟3開始,沒有建立則從步驟1開始。
  • 當有很是多的read節點在等待一個write節點刪除通知時,一旦write節點刪除,將會觸發很是多的read節點被調用,不過這種狀況沒法避免。

可撤銷和超時問題

當前的讀寫鎖並無考慮讀鎖可撤銷和超時問題,如何讓讀鎖主動放棄,如何判斷超時等,我想可行的方案仍是在客戶端本身處理,若是其餘客戶端想讓前面的節點放棄鎖,能夠在節點寫入unlock信息,讓持有鎖的客戶端監聽該變化,收到unlock信息,本身主動放棄對鎖的持有。

三、參考

[1] http://zookeeper.apache.org/doc/trunk/recipes.html#sc_recipes_Locks 
[2] http://blog.csdn.net/xubo_zhang/article/details/8506163 
[3] http://netcome.iteye.com/blog/1474255 一個比較好的介紹 
[4] http://blog.csdn.net/nimasike/article/details/51567653#reply 實現了一種非公平鎖

相關文章
相關標籤/搜索