Zookeeper實現分布式鎖

2021-08-31 09:34:15 字數 3964 閱讀 7679

zookeeper分布式鎖

1、分布式鎖

分布式鎖在一組程序之間提供互斥機制,任何時刻只有乙個程序可以持有鎖,持有鎖的程序就是系統的「領導者」。分布式鎖分為兩種:獨佔鎖和共享鎖:

獨佔鎖:所有嘗試獲取鎖的客戶端,最終只有乙個可以成功獲得鎖;

共享鎖:所有嘗試獲取鎖的客戶度,最終都會被執行,只是有個全域性時序。

2、zookeeper實現獨佔鎖

2.1、基本思路

利用znode名稱的唯一性,加鎖操作時,所有客戶端一起建立/***/lock節點,只有乙個建立成功,成功者獲得鎖。

2.1.1、存在的問題

2.1.1.1、連線丟失問題

(1)問題

此獨佔鎖演算法存在乙個問題:不能處理因連線丟失而導致的create操作失敗的問題。由於無法知道建立臨時znode的操作成功還是失敗,而且建立操作不是乙個冪等操作(多次請求結果一致,並且不會產生***),所以,如果第一次建立成功,重試會使得出現乙個永遠刪不掉的孤兒節點(至少在客戶端會話結束前),從而導致出現死鎖。

(2)解決方案

問題在於:重新連線後,客戶端無法判斷znode節點是否是自己建立的。

解決方案:在建立znode節點時,將客戶端的身份id寫入到znode資料區。客戶端的會話id是乙個長整數,並且在zookeekper服務中是唯一的,可以在連線丟失後用於識別客戶端。因此,在建立時,將客戶端的會話id作為身份標識,寫入znode中。在加鎖的時候,如果znode節點已存在,則讀取znode中儲存的身份id,與自身的會話id比較,如果一致就表示上一次建立成功,直接加鎖成功。

2.1.1.2、加鎖時間問題

(1) 問題

如果獲得鎖的客戶端加鎖時間足夠短,可能會存在加鎖失敗的客戶端呼叫介面zoo_wexists設定watcher失敗,從而導致客戶端加鎖失敗。

(2)解決方案

在設定watcher失敗時,判斷失敗原因是否是znonode,如果是,則進入重新建立znode流程,無需進入等待流程。

2.1.1.3、無法處理問題

由於加鎖的客戶端需要直接對鎖節點設定watch監控節點的建立/刪除,所以,如果成百上千的客戶端同時加鎖,那麼,當獲得鎖的客戶端釋放鎖時,zookeeper服務需要同時向成百上千的客戶端傳送watch事件,會對zookeeper服務產生壓力。

2.3、zookeeper實現獨佔鎖方案

基於上述思路,實現獨佔鎖的主要流程如下圖所示。

圖1 zookeeper實現獨佔鎖流程圖

3、zookeeper實現共享鎖

3.1、基本思路

3.1.1、概述

zookeeper中的順序節點,即:在/lock/目錄下建立3個節點,zookeeper會按照提起建立的順序來建立節點,節點分別為/lock/0000000001、/lock/0000000002和/lock/0000000003。

zookeeper中的臨時節點在客戶端與zookeeper集群斷開連線後會自動被刪除。

因此,我們可以利用zookeeper的臨時、順序節點來實現共享鎖,思路如下:

① 首先指定乙個作為鎖的znode,稱為/lock;

② 然後希望獲取鎖的客戶端在/lock目錄下建立臨時的順序znode,作為鎖znode的子節點;

③ 任何時刻,順序號最小的客戶端持有鎖;

例如:有兩個客戶端同時建立znode,分別是/lock/lock-1和/lock/lock-2,那麼建立/lock/lock-1的客戶端將持有鎖。zookeeper服務是順序的仲裁者,負責分配順序號。

④ 刪除znode(/lock/lock-1)即可釋放鎖;

⑤ 如果客戶端程序死亡,則對應的znode會自動被刪除;

⑥ 接下來建立/lock/lock-2的客戶端持有鎖,通過建立znode刪除的觀察,可以使客戶端在獲得鎖的時候得到通知。

3.1.2、存在的問題

3.1.2.1、羊群效應

(1)問題

如果有成百上千的客戶端在嘗試獲取鎖,每個客戶端都會在鎖znode上設定乙個watcher用於捕捉子節點的變化。每次鎖釋放或另乙個程序開始申請獲取鎖的時候,watcher都會被觸發,並且每個客戶度都會收到乙個通知。「羊群效應」是指大量客戶端收到同乙個事件通知,但是實際上只有部分需要處理此事件。在這種情況下,只有乙個客戶端會成功獲取鎖,但向客戶端傳送watch事件會產生峰值流量,對zookeeper服務造成壓力。

(2)解決方案

為了避免出現羊群效應,需要優化watcher觸發的條件。

在此方案中,只有前乙個順序號的子節點被刪除時才需要通知下乙個客戶端獲得鎖,而不是刪除(或建立)任何子節點時都需要通知。比如:

如果客戶端建立了znode:/lock/lock-1、/lock/lock-2和/lock/lock-3,那麼只有當/lock/lock-2被刪除時才需要通知建立/lock/lock-3的客戶端,而/lock/lock-1被刪除或新的節點/lock/lock-4被建立時,都不需要通知該客戶端。

3.1.2.2、連線丟失問題

(1)問題

此加鎖演算法存在乙個問題:不能處理因連線丟失而導致的create操作失敗的問題。由於無法知道建立順序znode的操作成功還是失敗,而且建立操作不是乙個冪等操作(多次請求結果一致,並且不會產生***),所以,如果第一次建立成功,重試會使得出現乙個永遠刪不掉的孤兒節點(至少在客戶端會話結束前),從而導致出現死鎖。

(2)解決方案

問題在於:在重新連線之後,客戶端不能判斷它是否已經建立過子節點。

解決方案:在znode的名稱中嵌入乙個id,如果客戶端出現連線丟失的情況,重新連線之後,先檢查鎖節點的所有子節點,看是否有子節點的名稱中包含其id。如果乙個子節點的名稱包含其id,則表示上一次建立操作成功,不需要再建立子節點;如果沒有子節點的名稱中包含其id,則客戶端需要建立乙個新的順序子節點。

客戶端的會話id是乙個長整數,並且在zookeekper服務中是唯一的,可以在連線丟失後用於識別客戶端。可以呼叫zookeekper c api中的zoo_client_id()獲取。

因此,在建立臨時的順序znode時,應當採用「lock--」這樣的命名方式,zookeeper在其尾部新增順序號之後,znode的名稱形式變成:lock--。由於順序號對於父節點來說是唯一的,因此,採用此命名方式可以使得子節點在保持建立順序的同時可以確認自己的建立者。

3.1.2.3、不可恢復異常

如果客戶端與zookeeper服務之間的會話過期,則它建立的znode將會被刪除,持有的鎖被釋放。由於鎖無法區分此時是正常的鎖釋放還是異常導致的鎖釋放,所以,對於此場景的鎖釋放,應用程式需要自己清理自身的狀態,然後重新嘗試申請新的鎖。

3.2、zookeeper實現共享鎖方案

對於加鎖操作,所有客戶端都在/lock目錄下建立臨時的順序節點,如果建立的客戶端發現自身建立節點序列號是/lock/目錄下最小的節點,則獲得鎖;否則,監視比自己建立節點的序列號小的節點,進入等待。

解鎖操作就是將自身建立的節點刪除即可。

具體演算法流程如下圖所示。

圖2 zookeeper實現共享鎖流程圖

Zookeeper實現分布式鎖

zookeeper實現分布式鎖 實現分布式環境下同步鎖的實現 author hao.wang date 2017 1 20 15 43 public class distributelockdemo implements watcher catch ioexception e catch inter...

Zookeeper實現分布式鎖

curatorframework提供的方法 方法名描述 create 開始建立操作,可以呼叫額外的方法 比如方式mode 或者後台執行background 並在最後呼叫forpath 指定要操作的znode delete 開始刪除操作.可以呼叫額外的方法 版本或者後台處理version or bac...

Zookeeper實現分布式鎖

一 分布式鎖介紹 分布式鎖主要用於在分布式環境中保護跨程序 跨主機 跨網路的共享資源實現互斥訪問,以達到保證資料的一致性。二 架構介紹 在介紹使用zookeeper實現分布式鎖之前,首先看當前的系統架構圖 解釋 左邊的整個區域表示乙個zookeeper集群,locker是zookeeper的乙個持久...