Zookeeper實現分佈式獨佔鎖

   由於項目是要部署到多個節點上進行運行的,並且沒有使用主備模式,使用的是主主模式,所以當兩個節點上不同進程操作同一資源的時候,需要一個分佈式鎖對資源進行加鎖處理。

    目前一般主流的方案都是使用redis來實現的,奈何當前項目處理的更多是離線數據而不是實時數據,基於業務考慮當前版本暫時沒有把redis引進來,所以只能基於ZK實現一個分佈式鎖(壓力不算太大,ZK還是抗的住的)。

    本來是打算參考github上他人的實現自己寫一個的,發下Curator中就有現成的,那就先直接用現成的好了。

 

//todo 插入使用的demo

可以看出來,Curator已經封裝的特別好了…

 

InterProcessMutex設計思想:

    InterProcessMutex是Curator實現的一種分佈式可重入排他鎖。該鎖實現的思想大致如下:

 

    所有的線程都去同一個ZK路徑下創建臨時有序類型節點(EPHEMERAL_SEQUENTIAL,序號是ZK根據當前子節點數量自動添加整數序號)。

    如果線程檢測到自身的節點序號最小,表明當前線程獲取到了鎖。其他節點會在序號比自己小的前一個節點上註冊一個Wacth,節點被刪除時能夠接收到通知,如果發現自己當前的序號是該路徑下最小的,表示自己獲取到的鎖,循環一直不停的執行該流程。

    有兩種情況會釋放鎖,第一種是正常情況下事務處理結束釋放鎖,第二種是ZK檢測到客戶端連接超時了,主動將節點給刪除了。無論上述哪種情況,客戶端都不應當再持有鎖了。

    (PS: 爲什麼不是往最小的節點註冊而是往比自己小的前一個節點上註冊,這樣做是爲了避免“羊羣效應”,如果有1000個線程都往最小的那個節點上註冊Watch,那麼鎖被釋放時,就會觸發往1000個線程發送通知,對ZK集羣會產生巨大的性能影響和網絡衝擊。由此也可以看出,這是一把公平鎖)

 

InterProcessMutex內部具體實現:

獲取鎖:

    內部調用internalLock()方法獲取鎖,如果獲取鎖期間與ZK服務端之間的連接發生異常,會拋出異常:

    -1和null表示獲取鎖期間會一直阻塞,當然也可以設置超時時間

 

看一下internalLock()方法:

內部有鎖重入邏輯

 

正確獲取鎖之後會在本地線程做一個記錄,然後返回true。真正加鎖的地方在attemptLock()方法內部:

 

點到attemptLock()方法內部看下:

createsTheLock()是在ZK路徑瞎創建臨時節點,注意此時並沒有獲取到鎖,核心其實就是一行代碼:

client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);

internalLockLoop()纔是加鎖,它判斷自己的節點序號是不是最小,是則返回true,表示獲取到鎖,否則阻塞等待

driver.getsTheLock用於判斷當前節點的序號是不是最小,返回的是前一個節點的序號(如果不是最小的話),還有是否當前節點是序號最小的標識符(true/false),如下所示:

如果當前節點序號是最小的,那麼predicateResults。getTheLock()得到的就是true,表明序號最小,可以持有鎖,否則回往欠一個節點上註冊WATCH,前一個節點鎖釋放了可以獲取鎖。

 

 

釋放鎖:

    如果鎖不被當前線程持有,調用release()方法會拋出異常,否則將重入次數減一。如果發現減一之後可重入次數已經變成0了,那麼刪除ZK上的臨時節點(表示鎖完全被釋放,其他線程可以搶佔鎖了)。

guaranteed()方法可以保證客戶端失聯的情況下,ZK服務器也能刪除臨時節點:

 

 

 

幾種異常情況說明:

    1.服務端手動把節點刪除了,客戶端還不知道,此時客戶端還可以重入麼???!!!這種算是異常情況,不是這把鎖要考慮的內容。

    可以測試一下這種異常情況

這個是線程打印出來的日誌,三個連續的“get”是手動刪除的結果,說明節點被手動刪除這種異常情況不是鎖要考慮的

 

    2.客戶端和服務端之間的網線被人拔了此時會怎麼處理???

    心跳超過一定時間,臨時節點就會被刪除掉了,這樣可以保證ZK服務端沒有問題,那麼客戶端呢?!客戶端還是認爲自己持有鎖麼?

    這個進程中的其他線程肯定不能持有鎖了,甚至這臺服務器上都不能持有這把鎖了。

    ------ 這種情況是不是隻有客戶端宕機了纔會發生,一般會不會考慮這種異常情況?!

    還是說這個版本的代碼有缺陷...

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-framework</artifactId>

            <version>2.12.0</version>

        </dependency>

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-recipes</artifactId>

            <version>2.7.1</version>

        </dependency>

 

 

 

參考:

    《從Paxos到Zookeeper 分佈式一致性原理與實踐》

    https://www.jianshu.com/p/bc5e24e4f614(Curator客戶端創建分析)

    https://www.jianshu.com/p/c2b4aa7a12f1(幾種分佈式鎖的實現方式)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章