【Go】zookeeper分布式鎖實(shí)現(xiàn)

分布式鎖的基本概念

:為了實(shí)現(xiàn)“同一時(shí)間,只能有一個(gè)實(shí)例對共享資源進(jìn)行訪問”

分布式鎖:當(dāng)多個(gè)分布在不同的機(jī)器上的進(jìn)程競爭共享資源時(shí)亏钩,就無法使用單機(jī)鎖實(shí)現(xiàn)同步烟零。此時(shí)就需要使用分布式鎖宁炫,而分布式鎖通常需要保存(或者說依賴于)第三方,常使用的第三方有 mysql龙亲,redis 和 zookeeper。

這里重點(diǎn)整理基于 zookeeper 的分布式鎖■基于 zookeeper 的實(shí)現(xiàn)也有兩種形式
1) 臨時(shí)節(jié)點(diǎn)
● 獲取鎖:多個(gè)實(shí)例去競爭創(chuàng)建同一個(gè)臨時(shí)節(jié)點(diǎn)杜耙,因?yàn)橥粋€(gè)臨時(shí)節(jié)點(diǎn)只可能存在一個(gè),因此只會有一個(gè)實(shí)例創(chuàng)建成功迎膜,而創(chuàng)建成功的實(shí)例視為獲取鎖成功泥技;其他的實(shí)例則監(jiān)聽該節(jié)點(diǎn)的變化
● 釋放鎖:利用臨時(shí)節(jié)點(diǎn)「會話中斷,節(jié)點(diǎn)刪除」的特點(diǎn)磕仅,實(shí)現(xiàn)鎖的釋放珊豹。

2) 臨時(shí)順序節(jié)點(diǎn)
● 獲取鎖:讓多個(gè)實(shí)例各自創(chuàng)建一個(gè) 臨時(shí)順序節(jié)點(diǎn),每次讓順序號最小的節(jié)點(diǎn)的實(shí)例持有分布式鎖榕订。讓其他實(shí)例監(jiān)聽最后一個(gè)序號比它小的節(jié)點(diǎn)店茶。
● 釋放鎖:同樣,利用臨時(shí)節(jié)點(diǎn)「會話中斷劫恒,節(jié)點(diǎn)刪除」的特點(diǎn)贩幻,實(shí)現(xiàn)鎖的釋放。

其中两嘴,基于 zookeeper 實(shí)現(xiàn)的分布式鎖有如下特點(diǎn):

  • zk本身是強(qiáng)一致性丛楚,非常試合作為分布式鎖

  • 實(shí)現(xiàn)簡單,且有現(xiàn)成的監(jiān)聽通知機(jī)制憔辫,就避免不斷輪詢鎖的狀態(tài)

  • 臨時(shí)節(jié)點(diǎn)的特性「會話中斷趣些,節(jié)點(diǎn)刪除」,使得鎖的釋放更簡單

  • 第一種實(shí)現(xiàn)方法存在一個(gè)問題「驚群」贰您,即由于大量實(shí)例監(jiān)聽一個(gè)節(jié)點(diǎn)坏平,當(dāng)該節(jié)點(diǎn)刪除時(shí),所有實(shí)例都會得到響應(yīng)锦亦,這種現(xiàn)狀非常不好舶替。因此存在大量實(shí)例競爭鎖時(shí),應(yīng)該采用第二種方案更好 [1]

實(shí)現(xiàn)Demo

實(shí)現(xiàn)依賴的開源包

demo 功能描述
● 啟動(dòng)三個(gè)實(shí)例競爭分布式鎖杠园,
● 當(dāng)獲得鎖的實(shí)例會訪問共享資源顾瞪,訪問完后會釋放鎖,并重新參與競爭

demo 實(shí)現(xiàn)代碼如下

package main

import (
    "fmt"
    "time"
    "github.com/docker/libkv"
    "github.com/docker/leadership"
    "github.com/docker/libkv/store"
    "github.com/docker/libkv/store/zookeeper"
    )

func init() {
    // step1:對使用的接口進(jìn)行注冊抛蚁,相當(dāng)于注冊驅(qū)動(dòng)
    // 如果不注冊一下陈醒,步驟3會失敗
    zookeeper.Register()
}

func main(){
    // step2:zookeeper相關(guān)信息配置
    // zookeeper的ip:port
    zkHost := []string {"127.0.0.1:2181"}
    // 每個(gè)實(shí)例將會在這個(gè)路徑下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
    path := "qconf/backup/zk_local"
    
    // step3:Create a store using pkg/store.
    client, err := libkv.NewStore("zk", zkHost, &store.Config{})
    if err != nil {
        panic(err)
    }

    // step4:創(chuàng)建一個(gè)競爭分布式鎖的候選者
    // 參數(shù)一:之前創(chuàng)建的storeClient
    // 參數(shù)二:競爭分布式鎖的路徑
    // 參數(shù)三:自我標(biāo)識
    // 參數(shù)四:超時(shí)時(shí)間
    candidate := leadership.NewCandidate(client, path, "underwood", 15*time.Second)
    
    // step5:執(zhí)行競爭分布式鎖
    // 返回的第一個(gè)通道:競爭結(jié)果
    // 返回的第二個(gè)通道:競爭時(shí)產(chǎn)生的錯(cuò)誤
    electedCh, errCh := candidate.RunForElection()

    for{
        // step6:阻塞等待結(jié)果
        var err error
        select{
        case err = <-errCh: // 競爭過程出現(xiàn)出錯(cuò)
            fmt.Printf("err=%v\n", err)
            break
        case isElected := <- electedCh: // 競爭結(jié)果出來了
            if isElected{ // 競爭成功,即獲得鎖篮绿。
                fmt.Println("i am the leader")
                time.Sleep(20*time.Second) // 模擬訪問共享數(shù)據(jù)
                fmt.Println("i give up leadership")
                candidate.Resign() // 釋放鎖孵延,然后重新參加競爭
            }else{ // 競爭失敗,即未獲得鎖亲配。有一種特殊情況尘应,任何實(shí)例競爭前都會將自己聲明為follower
                fmt.Println("i am the follower")
            }
        }

        if err != nil{
            break
        }
    }
}

測試結(jié)果

  • 第一個(gè)啟動(dòng)的實(shí)例
    第一個(gè)啟動(dòng)的實(shí)例的結(jié)果
  • 第二個(gè)啟動(dòng)的實(shí)例
    第二個(gè)啟動(dòng)的實(shí)例的結(jié)果
  • 第三個(gè)啟動(dòng)的實(shí)例
    第三個(gè)啟動(dòng)的實(shí)例的結(jié)果

PS:截取了部分結(jié)果惶凝,未截取部分才是截取部分的循環(huán)

底層源碼分析

底層實(shí)現(xiàn)原理:基于 zookeeper 的臨時(shí)順序節(jié)點(diǎn),實(shí)現(xiàn)分布式鎖犬钢。
● 順序號最小的節(jié)點(diǎn)所連接的實(shí)例苍鲜,獲得鎖
● 未獲得鎖的實(shí)例將監(jiān)聽“順序號比它小一號的節(jié)點(diǎn)的狀態(tài)”

競選邏輯:candidate.RunForElection()

競選邏輯

上面代碼最關(guān)鍵的在于 “l(fā)ock.Lock()”。由于本次 demo NewStore() 使用的是 zookeeper玷犹,因此 lock 對象是 zookeeperLock 實(shí)例對象混滔。所以,接下來要看 “zookeeperLock.Lock()”實(shí)現(xiàn)歹颓。主要的步驟包括(具體實(shí)現(xiàn)見下方代碼注釋)

  1. 創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
  2. 找出所有臨時(shí)順序節(jié)點(diǎn)的最小順序號以及當(dāng)前實(shí)例所連接的臨時(shí)順序節(jié)點(diǎn)的前一個(gè)順序號
  3. 如果當(dāng)前實(shí)例的順序號是最小坯屿,就獲得鎖;反之巍扛,阻塞等待
  4. 當(dāng)阻塞結(jié)束领跛,重新判斷一遍,以防止前一節(jié)點(diǎn)只是單純掉線導(dǎo)致節(jié)點(diǎn)刪除了
zookeeper獲取鎖的邏輯

參考:
[1] Zookeeper實(shí)現(xiàn)分布式鎖 - 簡書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撤奸,一起剝皮案震驚了整個(gè)濱河市吠昭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胧瓜,老刑警劉巖矢棚,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異府喳,居然都是意外死亡蒲肋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門劫拢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肉津,“玉大人强胰,你說我怎么就攤上這事舱沧。” “怎么了偶洋?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵熟吏,是天一觀的道長。 經(jīng)常有香客問我玄窝,道長牵寺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任恩脂,我火速辦了婚禮帽氓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俩块。我一直安慰自己黎休,他們只是感情好浓领,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著势腮,像睡著了一般联贩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捎拯,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天泪幌,我揣著相機(jī)與錄音,去河邊找鬼署照。 笑死祸泪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的建芙。 我是一名探鬼主播浴滴,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岁钓!你這毒婦竟也來了升略?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤屡限,失蹤者是張志新(化名)和其女友劉穎品嚣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钧大,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翰撑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啊央。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眶诈。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓜饥,靈堂內(nèi)的尸體忽然破棺而出逝撬,到底是詐尸還是另有隱情,我是刑警寧澤乓土,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布宪潮,位于F島的核電站,受9級特大地震影響趣苏,放射性物質(zhì)發(fā)生泄漏狡相。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一食磕、第九天 我趴在偏房一處隱蔽的房頂上張望尽棕。 院中可真熱鬧,春花似錦彬伦、人聲如沸滔悉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氧敢。三九已至日戈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孙乖,已是汗流浹背浙炼。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唯袄,地道東北人弯屈。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像恋拷,于是被迫代替她去往敵國和親资厉。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345