亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis鎖的過期時間小于業(yè)務(wù)的執(zhí)行時間如何續(xù)期

 更新時間:2024年05月22日 10:59:21   作者:養(yǎng)歌  
本文主要介紹了Redis鎖的過期時間小于業(yè)務(wù)的執(zhí)行時間如何續(xù)期,Redisson它能給Redis分布式鎖實現(xiàn)過期時間自動續(xù)期,具有一定的參考價值,感興趣的可以了解一下

前言

假設(shè)我們給鎖設(shè)置的過期時間太短,業(yè)務(wù)還沒執(zhí)行完成,鎖就過期了,這塊應(yīng)該如何處理呢?是否可以給分布式鎖續(xù)期?

解決方案:先設(shè)置一個過期時間,然后我們開啟一個守護線程,定時去檢測這個鎖的失效時間,如果鎖快要過期了,操作共享資源還未完成,那么就自動對鎖進行續(xù)期,重新設(shè)置過期時間。

幸運的是有一個庫把這些工作都幫我們封裝好了,那就是 Redisson,Redisson 是 java 語言實現(xiàn)的 Redis SDK 客戶端,它能給 Redis 分布式鎖實現(xiàn)過期時間自動續(xù)期。

當然,Redisson 不只是會做這個,除此之外,還封裝了很多易用的功能:

  • 可重入鎖
  • 樂觀鎖
  • 公平鎖
  • 讀寫鎖
  • Redlock

這里我們只講怎么實現(xiàn)續(xù)期,有需要的小伙伴可以自己去了解其他的功能哦。

在使用分布式鎖時,Redisson 采用了自動續(xù)期的方案來避免鎖過期,這個守護線程我們一般也把它叫做 “看門狗(watch dog)” 線程。

watch dog自動延期機制

只要客戶端一旦加鎖成功,就會啟動一個 watch dog 看門狗。watch dog 是一個后臺線程,會每隔 10 秒檢查一下,如果客戶端還持有鎖 key,那么就會不斷的延長鎖 key 的生存時間。

如果負責(zé)存儲這個分布式鎖的 Redission 節(jié)點宕機后,而且這個鎖正好處于鎖住的狀態(tài)時,這個鎖會出現(xiàn)鎖死的狀態(tài),為了避免這種情況的發(fā)生,Redisson 提供了一個監(jiān)控鎖的看門狗,它的作用是在 Redisson 實例被關(guān)閉前,不斷的延長鎖的有效期。默認情況下,看門狗的續(xù)期時間是 30 秒,也可以通過修改 Config.lockWatchdogTimeout 來指定。

在這里插入圖片描述

另外 Redisson 還提供了可以指定 leaseTime 參數(shù)的加鎖方法來指定加鎖的時間。超過這個時間后鎖便自動解開了,不會延長鎖的有效期。

接下來我們從源碼看一下是怎么實現(xiàn)的。

源碼分析

首先我們先寫一個 dome 一步步點擊進去看。

Config config = new Config();
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");

		RedissonClient redisson = Redisson.create(config);
		RLock lock = redisson.getLock("MyLock");

		lock.lock();

RLock lock = redisson.getLock(“MyLock”); 這句代碼就是為了獲取鎖的實例,然后我們可以看到它返回的是一個 RedissonLock 對象

    //name:鎖的名稱
    public RLock getLock(String name) {
    //默認創(chuàng)建的同步執(zhí)行器, (存在異步執(zhí)行器, 因為鎖的獲取和釋放是有強一致性要求, 默認同步)
        return new RedissonLock(this.connectionManager.getCommandExecutor(), name);
    }

點擊 RedissonLock 進去,發(fā)現(xiàn)這是一個 RedissonLock 構(gòu)造方法,主要初始化一些屬性。

    public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {
        super(commandExecutor, name);
        this.commandExecutor = commandExecutor;
       
        //唯一ID
        this.id = commandExecutor.getConnectionManager().getId();
        //等待獲取鎖時間
        this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
        //ID + 鎖名稱
        this.entryName = this.id + ":" + name;
        //發(fā)布訂閱
        this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub();
    }

我們點擊 getLockWatchdogTimeout() 進去看一下:

public class Config {
	
	private long lockWatchdogTimeout = 30 * 1000;
		
	public long getLockWatchdogTimeout() {
		return lockWatchdogTimeout;
	}
	
	//省略
}

從 internalLockLeaseTime 這個單詞也可以看出,這個加的分布式鎖的超時時間默認是 30 秒,現(xiàn)在我們知道默認是 30 秒,那么這個看門狗多久時間來延長一次有效期呢?我們接著往下看。

這里我們選擇 lock.lock(); 點擊進去看:

    public void lock() {
        try {
            this.lock(-1L, (TimeUnit)null, false);
        } catch (InterruptedException var2) {
            throw new IllegalStateException();
        }
    }
    private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
        long threadId = Thread.currentThread().getId();
        Long ttl = this.tryAcquire(leaseTime, unit, threadId);
        if (ttl != null) {
            RFuture<RedissonLockEntry> future = this.subscribe(threadId);
            if (interruptibly) {
                this.commandExecutor.syncSubscriptionInterrupted(future);
            } else {
                this.commandExecutor.syncSubscription(future);
            }

上面參數(shù)的含義:

leaseTime: 加鎖到期時間, -1 使用默認值 30 秒

unit: 時間單位, 毫秒、秒、分鐘、小時…

interruptibly: 是否可被中斷標示

而 this.tryAcquire()這個方法中是用來執(zhí)行加鎖, 繼續(xù)跳進去看:

    private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {
      //執(zhí)行 tryLock(...) 才會進入
        if (leaseTime != -1L) {
        //進行異步獲取鎖
            return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        } else {
         //嘗試異步獲取鎖, 獲取鎖成功返回空, 否則返回鎖剩余過期時間
            RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
            
         //ttlRemainingFuture 執(zhí)行完成后觸發(fā)此操作
            ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
                if (e == null) {
                //ttlRemaining == null 代表獲取了鎖
                //獲取到鎖后執(zhí)行續(xù)時操作
                    if (ttlRemaining == null) {
                        this.scheduleExpirationRenewal(threadId);
                    }

                }
            });
            return ttlRemainingFuture;
        }
    }

我們繼續(xù)選擇 scheduleExpirationRenewal() 跳進去看:

    private void scheduleExpirationRenewal(long threadId) {
        RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();
        RedissonLock.ExpirationEntry oldEntry = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);
        if (oldEntry != null) {
            oldEntry.addThreadId(threadId);
        } else {
            entry.addThreadId(threadId);
            this.renewExpiration();
        }

    }

接著進去 renewExpiration() 方法看:

該方法就是開啟定時任務(wù),也就是 watch dog 去進行鎖續(xù)期。

    private void renewExpiration() {
     //從容器中去獲取要被續(xù)期的鎖
        RedissonLock.ExpirationEntry ee = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());
         //容器中沒有要續(xù)期的鎖,直接返回null
        if (ee != null) {
        //創(chuàng)建定時任務(wù)
        //并且執(zhí)行的時間為 30000/3 毫秒,也就是 10 秒后
            Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
                public void run(Timeout timeout) throws Exception {
                //從容器中取出線程
                    RedissonLock.ExpirationEntry ent = (RedissonLock.ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName());
                    if (ent != null) {
                   
                        Long threadId = ent.getFirstThreadId();
                        if (threadId != null) {
                        //Redis進行鎖續(xù)期
                    //這個方法的作用其實底層也是去執(zhí)行LUA腳本
                            RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId);
                            //同理去處理Redis續(xù)命結(jié)果
                            future.onComplete((res, e) -> {
                                if (e != null) {
                                    RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e);
                                } else {
                                 //如果成功續(xù)期,遞歸繼續(xù)創(chuàng)建下一個 10S 后的任務(wù)
                                    if (res) {
                                    //遞歸繼續(xù)創(chuàng)建下一個10S后的任務(wù)
                                        RedissonLock.this.renewExpiration();
                                    }

                                }
                            });
                        }
                    }
                }
            }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
            ee.setTimeout(task);
        }
    }

從這里我們就知道,獲取鎖成功就會開啟一個定時任務(wù),也就是 watchdog 看門狗,定時任務(wù)會定期檢查去續(xù)期renewExpirationAsync(threadId)。

從這里我們明白,該定時調(diào)度每次調(diào)用的時間差是 internalLockLeaseTime / 3,也就是 10 秒。

總結(jié)

面試的時候簡單明了的回答這個問題就是:

只要客戶端一旦加鎖成功,就會啟動一個 watch dog 看門狗,他是一個后臺線程,會每隔 10 秒檢查一下,如果客戶端還持有鎖 key,那么就會不斷的延長鎖 key 的過期時間。

默認情況下,加鎖的時間是 30 秒,.如果加鎖的業(yè)務(wù)沒有執(zhí)行完,就會進行一次續(xù)期,把鎖重置成 30 秒,萬一業(yè)務(wù)的機器宕機了,那就續(xù)期不了,30 秒之后鎖就解開了。

到此這篇關(guān)于Redis鎖的過期時間小于業(yè)務(wù)的執(zhí)行時間如何續(xù)期的文章就介紹到這了,更多相關(guān)Redis 鎖續(xù)期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Redis中數(shù)值亂碼的根本原因以及解決方式

    詳解Redis中數(shù)值亂碼的根本原因以及解決方式

    這篇文章給大家詳細分析了Redis中數(shù)值亂碼的根本原因以及解決方式,通過代碼示例給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-02-02
  • Redis高并發(fā)防止秒殺超賣實戰(zhàn)源碼解決方案

    Redis高并發(fā)防止秒殺超賣實戰(zhàn)源碼解決方案

    本文主要介紹了Redis高并發(fā)防止秒殺超賣實戰(zhàn)源碼解決方案,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Redis常見限流算法原理及實現(xiàn)

    Redis常見限流算法原理及實現(xiàn)

    這篇文章主要介紹了Redis常見限流算法原理及實現(xiàn),限流簡稱流量限速(Rate?Limit)是指只允許指定的事件進入系統(tǒng),超過的部分將被拒絕服務(wù)、排隊或等待、降級等處理
    2022-08-08
  • Windows下搭建Redis集群的方法步驟

    Windows下搭建Redis集群的方法步驟

    本文主要介紹了Windows下搭建Redis集群的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Redis+Caffeine實現(xiàn)多級緩存的步驟

    Redis+Caffeine實現(xiàn)多級緩存的步驟

    隨著不斷的發(fā)展,這一架構(gòu)也產(chǎn)生了改進,在一些場景下可能單純使用Redis類的遠程緩存已經(jīng)不夠了,還需要進一步配合本地緩存使用,例如Guava cache或Caffeine,從而再次提升程序的響應(yīng)速度與服務(wù)性能,這篇文章主要介紹了Redis+Caffeine實現(xiàn)多級緩存,需要的朋友可以參考下
    2024-01-01
  • Redis如何存儲對象

    Redis如何存儲對象

    這篇文章主要介紹了Redis如何存儲對象,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Redis五種數(shù)據(jù)結(jié)構(gòu)在JAVA中如何封裝使用

    Redis五種數(shù)據(jù)結(jié)構(gòu)在JAVA中如何封裝使用

    本篇博文就針對Redis的五種數(shù)據(jù)結(jié)構(gòu)以及如何在JAVA中封裝使用做一個簡單的介紹。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • 一文搞懂Redis中String數(shù)據(jù)類型

    一文搞懂Redis中String數(shù)據(jù)類型

    string 是 redis 最基本的類型,你可以理解成與 Memcached 一模一樣的類型,一個 key 對應(yīng)一個 value。今天通過本文給大家介紹下Redis中String數(shù)據(jù)類型,感興趣的朋友一起看看吧
    2022-04-04
  • Redis緩存異常常用解決方案總結(jié)

    Redis緩存異常常用解決方案總結(jié)

    Redis緩存異常問題分別是緩存雪崩,緩存預(yù)熱,緩存穿透,緩存降級,緩存擊穿,本文主要介紹了Redis緩存異常常用解決方案總結(jié),具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • 基于Redis實現(xiàn)分布式鎖以及任務(wù)隊列

    基于Redis實現(xiàn)分布式鎖以及任務(wù)隊列

    這篇文章主要介紹了基于Redis實現(xiàn)分布式鎖以及任務(wù)隊列,需要的朋友可以參考下
    2015-11-11

最新評論