1. 程式人生 > >使用zookeeper實現分散式鎖

使用zookeeper實現分散式鎖

歡迎關注本人公眾號


大家也許都很熟悉了多個執行緒或者多個程序間的共享鎖的實現方式了,但是在分散式場景中我們會面臨多個Server之間的鎖的問題,實現的複雜度比較高。利用基於google chubby原理開發的開源的zookeeper,可以使得這個問題變得簡單很多。下面介紹幾種可能的實現方式,並且對比每種實現方式的優缺點。

1. 利用節點名稱的唯一性來實現共享鎖

ZooKeeper抽象出來的節點結構是一個和unix檔案系統類似的小型的樹狀的目錄結構。ZooKeeper機制規定:同一個目錄下只能有一個唯一的檔名。例如:我們在Zookeeper目錄/test目錄下建立,兩個客戶端建立一個名為Lock節點,只有一個能夠成功。

演算法思路: 利用名稱唯一性,加鎖操作時,只需要所有客戶端一起建立/test/Lock節點,只有一個建立成功,成功者獲得鎖。解鎖時,只需刪除/test/Lock節點,其餘客戶端再次進入競爭建立節點,直到所有客戶端都獲得鎖。

基於以上機制,利用節點名稱唯一性機制的共享鎖演算法流程如圖所示:


利用節點名稱的唯一性來實現共享鎖

該共享鎖實現很符合我們通常多個執行緒去競爭鎖的概念,利用節點名稱唯一性的做法簡明、可靠。

由上述演算法容易看出,由於客戶端會同時收到/test/Lock被刪除的通知,重新進入競爭建立節點,故存在"驚群現象"。

使用該方法進行測試鎖的效能列表如下:

Lock機制的互斥測試

總結 這種方案的正確性和可靠性是ZooKeeper機制保證的,實現簡單。缺點是會產生“驚群”效應,假如許多客戶端在等待一把鎖,當鎖釋放時候所有客戶端都被喚醒,僅僅有一個客戶端得到鎖。

2. 利用臨時順序節點實現共享鎖的一般做法

首先介紹一下,Zookeeper中有一種節點叫做順序節點,故名思議,假如我們在/lock/目錄下建立節3個點,ZooKeeper叢集會按照提起建立的順序來建立節點,節點分別為/lock/0000000001、/lock/0000000002、/lock/0000000003。

ZooKeeper中還有一種名為臨時節點的節點,臨時節點由某個客戶端建立,當客戶端與ZooKeeper叢集斷開連線,則開節點自動被刪除。

利用上面這兩個特性,我們來看下獲取實現分散式鎖的基本邏輯:

  • 客戶端呼叫create()方法建立名為“locknode/guid-lock-”的節點,需要注意的是,這裡節點的建立型別需要設定為EPHEMERAL_SEQUENTIAL。
  • 客戶端呼叫getChildren(“locknode”)方法來獲取所有已經建立的子節點,同時在這個節點上註冊上子節點變更通知的Watcher。
  • 客戶端獲取到所有子節點path之後,如果發現自己在步驟1中建立的節點是所有節點中序號最小的,那麼就認為這個客戶端獲得了鎖。
  • 如果在步驟3中發現自己並非是所有子節點中最小的,說明自己還沒有獲取到鎖,就開始等待,直到下次子節點變更通知的時候,再進行子節點的獲取,判斷是否獲取鎖。

釋放鎖的過程相對比較簡單,就是刪除自己建立的那個子節點即可。

上面這個分散式鎖的實現中,大體能夠滿足了一般的分散式叢集競爭鎖的需求。這裡說的一般性場景是指叢集規模不大,一般在10臺機器以內。

不過,細想上面的實現邏輯,我們很容易會發現一個問題,步驟4,“即獲取所有的子點,判斷自己建立的節點是否已經是序號最小的節點”,這個過程,在整個分散式鎖的競爭過程中,大量重複執行,並且絕大多數的執行結果都是判斷出自己並非是序號最小的節點,從而繼續等待下一次通知——這個顯然看起來不怎麼科學。客戶端無端的接受到過多的和自己不相關的事件通知,這如果在叢集規模大的時候,會對Server造成很大的效能影響,並且如果一旦同一時間有多個節點的客戶端斷開連線,這個時候,伺服器就會像其餘客戶端傳送大量的事件通知——這就是所謂的驚群效應。而這個問題的根源在於,沒有找準客戶端真正的關注點。

我們再來回顧一下上面的分散式鎖競爭過程,它的核心邏輯在於:判斷自己是否是所有節點中序號最小的。於是,很容易可以聯想的到的是,每個節點的建立者只需要關注比自己序號小的那個節點。

3. 利用臨時順序節點實現共享鎖的改進實現

下面是改進後的分散式鎖實現,和之前的實現方式唯一不同之處在於,這裡設計成每個鎖競爭者,只需要關注”locknode”節點下序號比自己小的那個節點是否存在即可。

演算法思路:對於加鎖操作,可以讓所有客戶端都去/lock目錄下建立臨時順序節點,如果建立的客戶端發現自身建立節點序列號是/lock/目錄下最小的節點,則獲得鎖。否則,監視比自己建立節點的序列號小的節點(比自己建立的節點小的最大節點),進入等待。

對於解鎖操作,只需要將自身建立的節點刪除即可。

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

利用臨時順序節點實現共享鎖

使用上述演算法進行測試的的結果如下表所示:

Lock機制的互斥測試

該演算法只監控比自身建立節點序列號小(比自己小的最大的節點)的節點,在當前獲得鎖的節點釋放鎖的時候沒有“驚群”。

總結 利用臨時順序節點來實現分散式鎖機制其實就是一種按照建立順序排隊的實現。這種方案效率高,避免了“驚群”效應,多個客戶端共同等待鎖,當鎖釋放時只有一個客戶端會被喚醒。

4. 使用menagerie

其實就是對方案3的一個封裝,不用自己寫程式碼了。直接拿來用就可以了。

menagerie基於Zookeeper實現了java.util.concurrent包的一個分散式版本。這個封裝是更大粒度上對各種分散式一致性使用場景的抽象。其中最基礎和常用的是一個分散式鎖的實現: org.menagerie.locks.ReentrantZkLock,通過ZooKeeper的全域性有序的特性和EPHEMERAL_SEQUENTIAL型別znode的支援,實現了分散式鎖。具體做法是:不同的client上每個試圖獲得鎖的執行緒,都在相同的basepath下面建立一個EPHEMERAL_SEQUENTIAL的node。EPHEMERAL表示要建立的是臨時znode,建立連線斷開時會自動刪除; SEQUENTIAL表示要自動在傳入的path後面綴上一個自增的全域性唯一字尾,作為最終的path。因此對不同的請求ZK會生成不同的字尾,並分別返回帶了各自後綴的path給各個請求。因為ZK全域性有序的特性,不管client請求怎樣先後到達,在ZKServer端都會最終排好一個順序,因此自增字尾最小的那個子節點,就對應第一個到達ZK的有效請求。然後client讀取basepath下的所有子節點和ZK返回給自己的path進行比較,當發現自己建立的sequential node的字尾序號排在第一個時,就認為自己獲得了鎖;否則的話,就認為自己沒有獲得鎖。這時肯定是有其他併發的並且是沒有斷開的client/執行緒先建立了node。

menagerie地址:https://github.com/openUtility/menagerie

參考資料以及推薦閱讀

相關推薦

分散式學習筆記七:基於zookeeper實現分散式

一、分散式鎖介紹         分散式鎖主要用於在分散式環境中保護跨程序、跨主機、跨網路的共享資源實現互斥訪問,以達到保證資料的一致性。 二、架構介紹     &nb

C# 基於ZooKeeper實現分散式

主體思路 1. 在locks節點下建立臨時順序節點node_n2. 判斷當前建立的節點是否為locks節點下所有子節點中最小的子節點3. 是則獲取鎖,進行業務處理,否則將節點從小到大排序,監聽當前節點上一個節點的刪除事件4. 事件觸發後回到步驟2進行判斷,直至拿到鎖 程式碼塊分析 建構函

使用redis和zookeeper實現分散式

1.分散式鎖   分散式鎖一般用在分散式系統或者多個應用中,用來控制同一任務是否執行或者任務的執行順序。在專案中,部署了多個tomcat應用,在執行定時任務時就會遇到同一任務可能執行多次的情況,我們可以藉助分散式鎖,保證在同一時間只有一個tomcat應用執行了定時任務。 &nb

zookeeper實現分散式及 如何避免羊群效應

問題導讀: 1.zookeeper如何實現分散式鎖? 2.什麼是羊群效應? 3.zookeeper如何釋放鎖? 在zookeeper應用場景有關於分散式叢集配置檔案同步問題的描述,設想一下如果有100臺機器同時對同一臺機器上某個檔案進行修改,如何才能保證文字不會被寫亂,這就

zookeeper 實現分散式

 實現互斥鎖 package com.zookeeper.lock; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.uti

【原創】redis庫存操作,分散式的四種實現方式[連載一]--基於zookeeper實現分散式

一、背景 在電商系統中,庫存的概念一定是有的,例如配一些商品的庫存,做商品秒殺活動等,而由於庫存操作頻繁且要求原子性操作,所以絕大多數電商系統都用Redis來實現庫存的加減,最近公司專案做架構升級,以微服務的形式做分散式部署,對庫存的操作也單獨封裝為一個微服務,這樣在高併發情況下,加減庫存時,就會出現超賣等

ZooKeeper完全解析(七) 使用ZooKeeper實現分散式之Java實現

  在上一節中,我們講了使用ZooKeeper來實現分散式鎖的原理,連結為  ZooKeeper完全解析(六) 使用ZooKeeper實現分散式鎖之實現原理 ,這一節我們來講一下如何使用Java來實現分散式鎖:   在實現原理中,我們把使用ZooKeeper實現分散式鎖分成

zookeeper實現分散式

zookeeper介紹 一種提供配置管理、分散式協同以及命名的中心化服務 Zookeeper提供一個多層級的節點名稱空間(節點稱為znode), 每個節點都用一個以斜槓(/)分隔的路徑表示,而且每個節點都有父節點(根節點除外),非常類似於檔案系統。 例如,/fo

基於zookeeper實現分散式

引言 在程式開發過程中不得不考慮的就是併發問題。在java中對於同一個jvm而言,jdk已經提供了lock和同步等。但是在分散式情況下,往往存在多個程序對一些資源產生競爭關係,而這些程序往往在不同的機器上,這個時候jdk中提供的已經不能滿足。分散式鎖顧明思議就是

Zookeeper 實現分散式(樂觀和悲觀)

說明: 做備忘用,大家之言彙總到一起。 Jar <!-- zkclient依賴 --> <dependency> <groupId>com.101tec</groupId> <art

分散式原始碼剖析(4) zookeeper實現分散式

zookeeper分散式鎖(curator) maven配置檔案: <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes<

Apache 開源的curator 基於Zookeeper實現分散式以及原始碼分析

前一段時間,我發表了一篇關於Redis實現分散式鎖 分散式環境下利用Redis實現分散式鎖,今天我帶領大家熟悉用zookeeper實現分散式鎖。 在學習分散式鎖之前,讓我們想一想,在什麼業務場景下會用到分散式鎖以及設計分散式鎖要注意什麼? 分散式鎖介紹 1、在什麼業務場

使用zookeeper實現分散式

歡迎關注本人公眾號 大家也許都很熟悉了多個執行緒或者多個程序間的共享鎖的實現方式了,但是在分散式場景中我們會面臨多個Server之間的鎖的問題,實現的複雜度比較高。利用基於google chubby原理開發的開源的zookeeper,可以使得這個問題變得簡單很多。下面

實戰 -- Zookeeper實現分散式

場景分析 比如購買商品的操作,首先獲取商品的庫存,判斷庫存是否充足,充足的話建立訂單減庫存,不充足的話不允許建立訂單。 有一款新型膝上型電腦,庫存只剩下1件的時候,有客戶A、B從不同的客戶端(比如網站和APP)上看中了這款電腦,兩人同時進行下單操作。 A和B同時獲取庫存

Java操作Zookeeper實現分散式、佇列

Zookeeper客戶端(Apache Curator) ZooKeeper常用客戶端 - zookeeper自帶的客戶端是官方提供的,比較底層、使用起來寫程式碼麻煩、不夠直接。 - Apache Curator是Apache的開源專案,封裝了zooke

springboot2.0.3整合zookeeper實現分散式

1.zookeeper安裝 dataVersion 可以作為樂觀鎖 1.1 zookeeper基礎知識 (1) session基本原理 ​ 客戶端與伺服器之間維持會話,每個會話都可以設定一個超時時間,心跳結束那麼會話就結束。心跳存在,那麼就是告訴伺服器別把我刪掉。

分散式】06-Zookeeper實現分散式:可重入原始碼分析

前言 前面已經講解了Redis的客戶端Redission是怎麼實現分散式鎖的,大多都深入到原始碼級別。 在分散式系統中,常見的分散式鎖實現方案還有Zookeeper,接下來會深入研究Zookeeper是如何來實現分散式鎖的。 Zookeeper初識 檔案系統 Zookeeper維護一個類似檔案系統的資料結構

zookeeper實現分散式總結,看這一篇足矣(設計模式應用實戰)

zk實現分散式鎖縱觀網路各種各樣的帖子層出不窮,筆者查閱很多資料發現一個問題,有些文章只寫原理並沒有具體實現,有些文章雖然寫了實現但是並不全面 借這個週末給大家做一個總結,程式碼拿來就可以用並且每一種實現都經過了測試沒有bug。下面我們先從最簡單的實現開始介紹: 簡單的實現 package com.sr

Springboot整合curator,實現分散式(zookeeper)

0.linux安裝啟動zookeeper yum install nc wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz tar -zxvf zookeepe

ZooKeeper分散式簡單實踐 利用Redis實現分散式

寫在最前面 前幾周寫了篇 利用Redis實現分散式鎖 ,今天簡單總結下ZooKeeper實現分散式鎖的過程。其實生產上我只用過Redis或者資料庫的方式,之前還真沒了解過ZooKeeper怎麼實現分散式鎖。這周簡單寫了個小Demo,更堅定了我繼續使用Redis的信心了。 ZooKeep