最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ZooKeeper框架教程Curator分布式鎖實現(xiàn)及源碼分析

 更新時間:2022年01月28日 14:45:04   作者:愛碼叔(稀有氣體)  
本文是ZooKeeper入門系列教程,本篇為大家介紹zookeeper一個優(yōu)秀的框架Curator,提供了各種分布式協(xié)調(diào)的服務,Curator中有著更為標準、規(guī)范的分布式鎖實現(xiàn)

ZooKeeper入門教程一簡介與核心概念

ZooKeeper入門教程二在單機和集群環(huán)境下的安裝搭建及使用

ZooKeeper入門教程三分布式鎖實現(xiàn)及完整運行源碼

上一篇文章中,我們使用zookeeper的java api實現(xiàn)了分布式排他鎖。

Curator中有著更為標準、規(guī)范的分布式鎖實現(xiàn)。與其我們自己去實現(xiàn),不如直接使用Curator。通過學習Curator的源代碼,我們也能了解實現(xiàn)分布式鎖的最佳實踐。

Curator中有各種分布式鎖,本文挑選其中一個---InterProcessMutex進行講解。

我們先看一下Curator代碼中對于InterProcessMutex的注釋:

可重入的互斥鎖,跨JVM工作。使用ZooKeeper來控制鎖。所有JVM中的任何進程,只要使用同樣的鎖路徑,將會成為跨進程的一部分。此外,這個排他鎖是“公平的”,每個用戶按照申請的順序得到排他鎖。

可見InterProcessMutex和我們自己實現(xiàn)的例子都是一個排他鎖,此外還可以重入。 

  如何使用InterProcessMutex

在分析InterProcessMutex代碼前,我們先看一下它是如何使用的,下面代碼簡單展示了InterProcessMutex的使用:

    public static void soldTickWithLock(CuratorFramework client) throws Exception {
        //創(chuàng)建分布式鎖, 鎖空間的根節(jié)點路徑為/curator/lock
        InterProcessMutex mutex = new InterProcessMutex(client, "/curator/locks");
        mutex.acquire();
 
        //獲得了鎖, 進行業(yè)務流程
        //代表復雜邏輯執(zhí)行了一段時間
        int sleepMillis = (int) (Math.random() * 2000);
        Thread.sleep(sleepMillis);
 
        //完成業(yè)務流程, 釋放鎖
        mutex.release();
    }

使用方式和我們自己編寫的鎖是一樣的,首先通過mutex.acquire()獲取鎖,該方法會阻塞進程,直到獲取鎖,然后執(zhí)行你的業(yè)務方法,最后通過 mutex.release()釋放鎖。

接下來我們進入正題,展開分析Curator關于分布式鎖的實現(xiàn):

  實現(xiàn)思路

Curator設計方式和之前我們自己實現(xiàn)的方式是類似的:

1、創(chuàng)建有序臨時節(jié)點

2、觸發(fā)“嘗試取鎖邏輯”,如果自己是臨時鎖節(jié)點序列的第一個,則取得鎖,獲取鎖成功。

3、如果自己不是序列中第一個,則監(jiān)聽前一個鎖節(jié)點變更。同時阻塞線程。

4、當前一個鎖節(jié)點變更時,通過watcher恢復線程,然后再次到步驟2“嘗試取鎖邏輯”

如下圖所示:

   代碼實現(xiàn)概述

Curator對于排它鎖的頂層實現(xiàn)邏輯在InterProcessMutex類中,它對客戶端暴露鎖的使用方法,如獲取鎖和釋放鎖等。但鎖的上述實現(xiàn)邏輯,是由他持有的LockInternals對象來具體實現(xiàn)的。LockInternals使用StandardLockInternalsDriver類中的方法來做一些處理。

簡單點解釋,我們打個比方,Curator好比是一家公司承接各種業(yè)務,InterProcessMutex是老板,收到自己客戶(client)的需求后,分配給自己的下屬LockInternals去具體完成,同時給他一個工具StandardLockInternalsDriver,讓他在做任務的過程中使用。如下圖展示:

接下來我們將深入分析InterProcessMutex、LockInternals及StandardLockInternalsDriver類。

  InterProcessMutex源碼分析

InterProcessMutex類是curator中的排它鎖類,客戶端直接打交道的就是InterProcessMutex。所以我們從頂層開始,先分析InterProcessMutex。

  實現(xiàn)接口

InterProcessMutex實現(xiàn)了兩個接口:

public class InterProcessMutex implements InterProcessLock, Revocable<InterProcessMutex>

InterProcessLock是分布式鎖接口,分布式鎖必須實現(xiàn)接口中的如下方法:

1、獲取鎖,直到鎖可用

public void acquire() throws Exception;

2、在指定等待的時間內(nèi)獲取鎖。

public boolean acquire(long time, TimeUnit unit) throws Exception;

3、釋放鎖

public void release() throws Exception;

4、當前線程是否獲取了鎖

boolean isAcquiredInThisProcess();

以上方法也是InterProcessMutex暴露出來,供客戶端在使用分布式鎖時調(diào)用。

Revocable<T>,實現(xiàn)該接口的鎖,鎖是可以被撤銷的。本編文章重點講解鎖的實現(xiàn)機制,關于撤銷部分不做討論。

  屬性

InterProcessMutex屬性如下:

類型名稱說明
LockInternalsinternals鎖的實現(xiàn)都在該類中,InterProcessMutex通過此類的方法實現(xiàn)鎖
StringbasePath鎖節(jié)點在zk中的根路徑
ConcurrentMap<Thread, LockData>threadData線程和自己的鎖相關數(shù)據(jù)映射
StringLOCK_NAME常量,值為"lock-"。表示鎖節(jié)點的前綴

它還有一個內(nèi)部靜態(tài)類LockData,也是threadData中保存的value,它定義了鎖的相關數(shù)據(jù),包括鎖所屬線程,鎖的全路徑,和該線程加鎖的次數(shù)(InterProcessMutex為可重入鎖)。代碼如下:

private static class LockData
{
    final Thread owningThread;
    final String lockPath;
    final AtomicInteger lockCount = new AtomicInteger(1);
    private LockData(Thread owningThread, String lockPath)
    {
        this.owningThread = owningThread;
        this.lockPath = lockPath;
    }
}

  構(gòu)造方法

InterProcessMutex有三個構(gòu)造方法,根據(jù)入?yún)⒉煌短渍{(diào)用,最終調(diào)用的構(gòu)造方法如下:

InterProcessMutex(CuratorFramework client, String path, String lockName, int maxLeases, LockInternalsDriver driver)
{
    basePath = PathUtils.validatePath(path);
    internals = new LockInternals(client, driver, path, lockName, maxLeases);
}

可見構(gòu)造方法最終初始化了兩個屬性,basePath被設置為我們傳入的值 "/curator/lock",這是鎖的根節(jié)點。此外就是初始化了internals,前面說過internals是真正實現(xiàn)鎖功能的對象。真正干活的是internals。

構(gòu)造完InterProcessMutex對象后,我們看看它是如何工作的。

  方法

InterProcessMutex實現(xiàn)InterProcessLock接口,關于分布式鎖的幾個方法都在這個接口中,我們看看InterProcessMutex是如何實現(xiàn)的。

  獲得鎖

獲得鎖有兩個方法,區(qū)別為是否限定了等待鎖的時間長度。其實最終都是調(diào)用的私有方法internalLock()。不限定等待時長的代碼如下:

public void acquire() throws Exception
{
    if ( !internalLock(-1, null) )
    {
        throw new IOException("Lost connection while trying to acquire lock: " + basePath);
    }
}

可以看到internalLock()返回false時,只可能因為連接超時,否則會一直等待獲取鎖。

internalLock邏輯如下:

  • 取得當前線程在threadData中的lockData
  • 如果存在該線程的鎖數(shù)據(jù),說明是鎖重入, lockData.lockCount加1,直接返回true。獲取鎖成功
  • 如果不存在該線程的鎖數(shù)據(jù),則通過internals.attemptLock()獲取鎖,此時線程被阻塞,直至獲得到鎖
  • 鎖獲取成功后,把鎖的信息保存到threadData中。
  • 如果沒能獲取到鎖,則返回false。

完整代碼如下:

private boolean internalLock(long time, TimeUnit unit) throws Exception
{
    /*
       Note on concurrency: a given lockData instance
       can be only acted on by a single thread so locking isn't necessary
    */
    Thread currentThread = Thread.currentThread();
    LockData lockData = threadData.get(currentThread);
    if ( lockData != null )
    {
        // re-entering
        lockData.lockCount.incrementAndGet();
        return true;
    }
    String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
    if ( lockPath != null )
    {
        LockData newLockData = new LockData(currentThread, lockPath);
        threadData.put(currentThread, newLockData);
        return true;
    }
    return false;
}

可以看到獲取鎖的核心代碼是internals.attemptLock

  釋放鎖

釋放鎖的方法為release(),邏輯如下:

從threadData中取得當前線程的鎖數(shù)據(jù),有如下情況:

不存在,拋出無此鎖的異常

存在,而且lockCount-1后大于零,說明該線程鎖重入了,所以直接返回,并不在zk中釋放。

存在,而且lockCount-1后小于零,說明有某種異常發(fā)生,直接拋異常

存在,而且lockCount-1等于零,這是無重入的正確狀態(tài),需要做的就是從zk中刪除臨時節(jié)點,通過internals.releaseLock(),不管結(jié)果如何,在threadData中移除該線程的數(shù)據(jù)。 

InterProcessMutex小結(jié)

分布式鎖主要用到的是上面兩個方法,InterProcessMutex還有些其他的方法,這里就不做具體講解,可以自己看一下,實現(xiàn)都不復雜。

通過對InterProcessMutex的講解,相信我們已經(jīng)對鎖的獲得和釋放有了了解,應該也意識到真正實現(xiàn)鎖的是LockInternals類。接下來我們將重點講解LockInternals。

  LockInternals源碼分析

Curator通過zk實現(xiàn)分布式鎖的核心邏輯都在LockInternals中,我們按獲取鎖到釋放鎖的流程為指引,逐步分析LockInternals的源代碼。

  獲取鎖

在InterProcessMutex獲取鎖的代碼分析中,可以看到它是通過internals.attemptLock(time, unit, getLockNodeBytes());來獲取鎖的,那么我們就以這個方法為入口。此方法的邏輯比較簡單,如下:

通過driver在zk上創(chuàng)建鎖節(jié)點,獲得鎖節(jié)點路徑。

通過internalLockLoop()方法阻塞進程,直到獲取鎖成功。

核心代碼如下:

ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);

我們繼續(xù)分析internalLockLoop方法,獲取鎖的核心邏輯在此方法中。

internalLockLoop中通過while自旋,判斷鎖如果沒有被獲取,將不斷的去嘗試獲取鎖。

while循環(huán)中邏輯如下:

  • 通過driver查看當前鎖節(jié)點序號是否排在第一位,如果排在第一位,說明取鎖成功,跳出循環(huán)
  • 如果沒有排在第一位,則監(jiān)聽自己的前序鎖節(jié)點,然后阻塞線程。

當前序節(jié)點釋放了鎖,監(jiān)聽會被觸發(fā),恢復線程,此時主線程又回到while中第一步。

重復以上邏輯,直至獲取到鎖(自己鎖的序號排在首位)。

internalLockLoop方法核心代碼如下:

while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock )
{
    List<String>        children = getSortedChildren();
    String              sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash
 
    PredicateResults    predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
    if ( predicateResults.getsTheLock() )
    {
        haveTheLock = true;
    }
    else
    {
        String  previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();
 
        synchronized(this)
        {
            try 
            {
                // use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
                client.getData().usingWatcher(watcher).forPath(previousSequencePath);
                if ( millisToWait != null )
                {
                    millisToWait -= (System.currentTimeMillis() - startMillis);
                    startMillis = System.currentTimeMillis();
                    if ( millisToWait <= 0 )
                    {
                        doDelete = true;    // timed out - delete our node
                        break;
                    }
 
                    wait(millisToWait);
                }
                else
                {
                    wait();
                }
            }
            catch ( KeeperException.NoNodeException e ) 
            {
                // it has been deleted (i.e. lock released). Try to acquire again
            }
        }
    }
}

獲取鎖的主要代碼邏輯我們到這就已經(jīng)分析完了,可見和我們自己的實現(xiàn)還是基本一樣的。此外上面提到了driver對象,也就是StandardLockInternalsDriver類,它提供了一些輔助的方法,比如說在zk創(chuàng)建鎖節(jié)點,判斷zk上鎖序列第一位是否為當前鎖,鎖序列的排序邏輯等。我們就不具體講解了。

  釋放鎖

釋放鎖的邏輯很簡單,移除watcher,刪除鎖節(jié)點。代碼如下:

final void releaseLock(String lockPath) throws Exception
{
client.removeWatchers();
revocable.set(null);
deleteOurPath(lockPath);
}

  總結(jié)

至此,Curator中InterProcessMutex的源代碼分析全部完成。

簡單回顧下,InterProcessMutex類封裝上層邏輯,對外暴露鎖的使用方法。而真正的鎖實現(xiàn)邏輯在LockInternals中,它通過對zk臨時有序鎖節(jié)點的創(chuàng)建和監(jiān)控,判斷自己的鎖序號是否在首位,來實現(xiàn)鎖的獲取。此外它還結(jié)合StandardLockInternalsDriver提供的方法,共同實現(xiàn)了排他鎖。

希望大家以后多多支持腳本之家!

相關文章

  • 詳解Java利用同步塊synchronized()保證并發(fā)安全

    詳解Java利用同步塊synchronized()保證并發(fā)安全

    這篇文章主要介紹了Java利用同步塊synchronized()保證并發(fā)安全,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • 如何手寫一個Spring Boot Starter

    如何手寫一個Spring Boot Starter

    這篇文章主要介紹了如何手寫一個Spring Boot Starter,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下
    2021-03-03
  • java swing實現(xiàn)電影購票系統(tǒng)

    java swing實現(xiàn)電影購票系統(tǒng)

    這篇文章主要為大家詳細介紹了java swing實現(xiàn)電影購票系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Java中使用Jedis操作Redis的示例代碼

    Java中使用Jedis操作Redis的示例代碼

    本篇文章主要介紹了Java中使用Jedis操作Redis的示例代碼,具有一定的參考價值,有興趣的可以了解一下。
    2016-12-12
  • Java中的關鍵字volatile詳解

    Java中的關鍵字volatile詳解

    這篇文章主要介紹了Java中的關鍵字volatile,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • aop注解方式實現(xiàn)全局日志管理方法

    aop注解方式實現(xiàn)全局日志管理方法

    下面小編就為大家分享一篇aop注解方式實現(xiàn)全局日志管理方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • mybatis的MappedStatement線程安全探究

    mybatis的MappedStatement線程安全探究

    這篇文章主要為大家介紹了mybatis的MappedStatement線程安全示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Java獲取項目路徑的多種方式

    Java獲取項目路徑的多種方式

    這篇文章主要介紹了Java獲取項目路徑的多種方式,這時候就需要用java給我們提供的一些獲取相對路徑方法了,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2022-01-01
  • SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片

    SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片

    這篇文章主要介紹了SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片,文中通過圖文和代碼講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-12-12
  • IDEA打開java項目后里面的java文件不能運行解決辦法

    IDEA打開java項目后里面的java文件不能運行解決辦法

    這篇文章主要給大家介紹了關于IDEA打開java項目后里面的java文件不能運行的解決辦法,有時候想運行別人的項目,但是別人的項目并非IDEA項目(甚至只有源碼),當我們打開項目時候,并不能運行,需要的朋友可以參考下
    2023-10-10

最新評論

绵竹市| 开化县| 岳阳市| 柘荣县| 阳西县| 湾仔区| 洪泽县| 开江县| 濮阳市| 丽江市| 秦安县| 定结县| 额济纳旗| 沁阳市| 阜宁县| 桐梓县| 巩义市| 蒙自县| 依安县| 光泽县| 仁寿县| 天柱县| 浦县| 长武县| 修文县| 张掖市| 顺昌县| 绥棱县| 南宁市| 柏乡县| 安溪县| 吉林省| 自贡市| 邮箱| 湟中县| 蓝田县| 桐柏县| 思南县| 宜宾县| 柯坪县| 安达市|