溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

ZooKeeper中如何實(shí)現(xiàn)分布式鎖

發(fā)布時(shí)間:2021-06-25 16:45:07 來源:億速云 閱讀:125 作者:Leah 欄目:編程語(yǔ)言

ZooKeeper中如何實(shí)現(xiàn)分布式鎖,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

ZooKeeper 是一個(gè)典型的分布式數(shù)據(jù)一致性解決方案,分布式應(yīng)用程序可以基于 ZooKeeper 實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡、分布式協(xié)調(diào)/通知、集群管理、Master 選舉、分布式鎖等功能。

節(jié)點(diǎn)

在介紹 ZooKeeper 分布式鎖前需要先了解一下 ZooKeeper 中節(jié)點(diǎn)(Znode),ZooKeeper 的數(shù)據(jù)存儲(chǔ)數(shù)據(jù)模型是一棵樹(Znode Tree),由斜杠(/)的進(jìn)行分割的路徑,就是一個(gè) Znode(如 /locks/my_lock)。每個(gè) Znode 上都會(huì)保存自己的數(shù)據(jù)內(nèi)容,同時(shí)還會(huì)保存一系列屬性信息。

Znode 又分為以下四種類型:

持久節(jié)點(diǎn)      節(jié)點(diǎn)創(chuàng)建后,會(huì)一直存在,不會(huì)因客戶端會(huì)話失效而刪除              持久順序節(jié)點(diǎn)      基本特性與持久節(jié)點(diǎn)一致,創(chuàng)建節(jié)點(diǎn)的過程中,ZooKeeper 會(huì)在其名字后自動(dòng)追加一個(gè)單調(diào)增長(zhǎng)的數(shù)字后綴,作為新的節(jié)點(diǎn)名              臨時(shí)節(jié)點(diǎn)      客戶端會(huì)話失效或連接關(guān)閉后,該節(jié)點(diǎn)會(huì)被自動(dòng)刪除              臨時(shí)順序節(jié)點(diǎn)      基本特性與臨時(shí)節(jié)點(diǎn)一致,創(chuàng)建節(jié)點(diǎn)的過程中,ZooKeeper 會(huì)在其名字后自動(dòng)追加一個(gè)單調(diào)增長(zhǎng)的數(shù)字后綴,作為新的節(jié)點(diǎn)名

鎖原理

ZooKeeper 分布式鎖是基于 臨時(shí)順序節(jié)點(diǎn) 來實(shí)現(xiàn)的,鎖可理解為 ZooKeeper 上的一個(gè)節(jié)點(diǎn),當(dāng)需要獲取鎖時(shí),就在這個(gè)鎖節(jié)點(diǎn)下創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn)。當(dāng)存在多個(gè)客戶端同時(shí)來獲取鎖,就按順序依次創(chuàng)建多個(gè)臨時(shí)順序節(jié)點(diǎn),但只有排列序號(hào)是第一的那個(gè)節(jié)點(diǎn)能獲取鎖成功,其他節(jié)點(diǎn)則按順序分別監(jiān)聽前一個(gè)節(jié)點(diǎn)的變化,當(dāng)被監(jiān)聽者釋放鎖時(shí),監(jiān)聽者就可以馬上獲得鎖。

而且用臨時(shí)順序節(jié)點(diǎn)的另外一個(gè)用意是如果某個(gè)客戶端創(chuàng)建臨時(shí)順序節(jié)點(diǎn)后,自己意外宕機(jī)了也沒關(guān)系,ZooKeeper 感知到某個(gè)客戶端宕機(jī)后會(huì)自動(dòng)刪除對(duì)應(yīng)的臨時(shí)順序節(jié)點(diǎn),相當(dāng)于自動(dòng)釋放鎖。

如上圖:ClientA 和 ClientB 同時(shí)想獲取鎖,所以都在 locks 節(jié)點(diǎn)下創(chuàng)建了一個(gè)臨時(shí)節(jié)點(diǎn) 1 和 2,而 1 是當(dāng)前 locks 節(jié)點(diǎn)下排列序號(hào)第一的節(jié)點(diǎn),所以 ClientA 獲取鎖成功,而 ClientB 處于等待狀態(tài),這時(shí) ZooKeeper 中的 2 節(jié)點(diǎn)會(huì)監(jiān)聽 1 節(jié)點(diǎn),當(dāng) 1節(jié)點(diǎn)鎖釋放(節(jié)點(diǎn)被刪除)時(shí),2 就變成了 locks 節(jié)點(diǎn)下排列序號(hào)第一的節(jié)點(diǎn),這樣 ClientB 就獲取鎖成功了。

代碼測(cè)試

請(qǐng)確保 ZooKeeper 服務(wù)已啟動(dòng),ZooKeeper 的搭建可參考Kafka 集群 中的 ZooKeeper 集群部分

以下是基于 C# 的測(cè)試,Java 可使用 Curator 框架,實(shí)現(xiàn)原理和上面描述是一致的,有興趣可以看看源碼,應(yīng)該也不難理解。

創(chuàng)建 .NET Core 控制臺(tái)程序 Nuget

安裝 ZooKeeperNetEx.Recipes

創(chuàng)建 ZooKeeper Client

private const int CONNECTION_TIMEOUT = 50000;private const string CONNECTION_STRING = "127.0.0.1:2181";private ZooKeeper CreateClient(){var zooKeeper = new ZooKeeper(CONNECTION_STRING, CONNECTION_TIMEOUT, NullWatcher.Instance);Stopwatch sw = new Stopwatch();sw.Start();while (sw.ElapsedMilliseconds < CONNECTION_TIMEOUT){var state = zooKeeper.getState();if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTING){break;}}sw.Stop();return zooKeeper;}class NullWatcher : Watcher  {    public static readonly NullWatcher Instance = new NullWatcher();    private NullWatcher() { }    public override Task process(WatchedEvent @event)    {      return Task.CompletedTask;    }  }



類型描述

添加 Lock 方法

/// <summary>/// 加鎖/// </summary>/// <param name="key">加鎖的節(jié)點(diǎn)名</param>/// <param name="lockAcquiredAction">加鎖成功后需要執(zhí)行的邏輯</param>/// <param name="lockReleasedAction">鎖釋放后需要執(zhí)行的邏輯,可為空</param>/// <returns></returns>public async Task Lock(string key, Action lockAcquiredAction, Action lockReleasedAction = null){// 獲取 ZooKeeper ClientZooKeeper keeper = CreateClient();// 指定鎖節(jié)點(diǎn)WriteLock writeLock = new WriteLock(keeper, $"/{key}", null);var lockCallback = new LockCallback(() =>{lockAcquiredAction.Invoke();writeLock.unlock();}, lockReleasedAction);// 綁定鎖獲取和釋放的監(jiān)聽對(duì)象writeLock.setLockListener(lockCallback);// 獲取鎖(獲取失敗時(shí)會(huì)監(jiān)聽上一個(gè)臨時(shí)節(jié)點(diǎn))await writeLock.Lock();}class LockCallback : LockListener{private readonly Action _lockAcquiredAction;private readonly Action _lockReleasedAction;public LockCallback(Action lockAcquiredAction, Action lockReleasedAction){_lockAcquiredAction = lockAcquiredAction;_lockReleasedAction = lockReleasedAction;}/// <summary>/// 獲取鎖成功回調(diào)/// </summary>/// <returns></returns>public Task lockAcquired(){_lockAcquiredAction?.Invoke();return Task.FromResult(0);}/// <summary>/// 釋放鎖成功回調(diào)/// </summary>/// <returns></returns>public Task lockReleased(){_lockReleasedAction?.Invoke();return Task.FromResult(0);}}

多線程模擬測(cè)試

static async Task RunAsync(){Parallel.For(1, 10, async (i) =>{await new ZooKeeprDistributedLock().Lock("locks", () =>{Console.WriteLine($"第{i}個(gè)請(qǐng)求,獲取鎖成功:{DateTime.Now},線程Id:{Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(1000); // 業(yè)務(wù)邏輯...}, () =>{Console.WriteLine($"第{i}個(gè)請(qǐng)求,釋放鎖成功:{DateTime.Now},線程Id:{Thread.CurrentThread.ManagedThreadId}");Console.WriteLine("-------------------------------");});});await Task.CompletedTask;}

看完上述內(nèi)容,你們掌握Z(yǔ)ooKeeper中如何實(shí)現(xiàn)分布式鎖的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI