日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

redisson MultiLock原理及分布式锁的应用

發(fā)布時(shí)間:2025/3/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redisson MultiLock原理及分布式锁的应用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前言

基于 Redis 的 Redisson 分布式聯(lián)鎖 RedissonMultiLock 對(duì)象可以將多個(gè) RLock 對(duì)象關(guān)聯(lián)為一個(gè)聯(lián)鎖,每個(gè) RLock 對(duì)象實(shí)例可以來(lái)自于不同的 Redisson 實(shí)例。當(dāng)然,這是官網(wǎng)的介紹,具體是什么?一起看看聯(lián)鎖 MultiLock 使用以及源碼吧!

二、MultiLock 使用

按照官方文檔的說(shuō)法,這里 Redisson 客戶端可以不是同一個(gè)。當(dāng)然,一般工作中也不會(huì)說(shuō)不用一個(gè)客戶端吧。

三、加鎖

源碼入口:org.redisson.RedissonMultiLock#lock(),默認(rèn)超時(shí)時(shí)間 leaseTime 沒有設(shè)置,所以為 -1。

public void lock(long leaseTime, TimeUnit unit) {try {lockInterruptibly(leaseTime, unit);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}


這塊方法太長(zhǎng),咱們拆分進(jìn)行閱讀。

  • 基礎(chǔ)等待時(shí)間 baseWaitTime = 鎖數(shù)量 * 1500,在這里就是 4500 毫秒;
  • leaseTime == -1 所以 waitTime = baseWaitTime,也就是 4500;
  • while (true) 調(diào)用 tryLock 加鎖,直到成功。
  • 調(diào)用 tryLock 方法,其中參數(shù) waitTime = 4500,leaseTime = -1,unit = MILLISECONDS。

    下面看一下 tryLock 里面有什么邏輯?


    leaseTime != -1 不滿足,這部分直接跳過(guò)。


    waitTime != -1 條件滿足,remainTime = 4500,lockWaitTime = 4500。


    所以,failedLocksLimit() 這個(gè)方法直接返回 0,就是必須全部加鎖成功。

    這里才是重點(diǎn):遍歷所有的鎖,依次加鎖。加鎖邏輯就和可重入鎖加鎖并無(wú)區(qū)別了。所以 Lua 腳本就不進(jìn)行分析了。

    上面就是 tryLock 加鎖之后的結(jié)果。

    加鎖成功,則將成功的鎖放進(jìn) acquiredLocks 集合中;加鎖失敗,需要判斷 failedLocksLimit,因?yàn)檫@里是 0,所以會(huì)直接對(duì)成功加鎖集合 acquiredLocks 中的所有鎖執(zhí)行鎖釋放,同時(shí)清空成功集合,恢復(fù)迭代器。

    每次加鎖之后,會(huì)更新鎖剩余時(shí)間 remainTime,如果 remainTime 小于等于 0 了,則說(shuō)明加鎖超時(shí),直接返回 false。這樣就會(huì)執(zhí)行外部的 while (true) 邏輯,然后重新再走一遍 RedissonMultiLock#tryLock。

    • 總結(jié)
      根據(jù)理解,畫圖如下:總體而言,就是將 key1、key2、key3 …… keyN 放到一個(gè) List 集合中,然后迭代循環(huán)加鎖,直到所有的都成功。

    • lock和tryLock的區(qū)別
  • tryLock() 它表示用來(lái)嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false
  • tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過(guò)區(qū)別在于這個(gè)方法在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true
  • tryLock(long waitTime, long leaseTime, TimeUnit unit)在2的基礎(chǔ)上,如果獲取到鎖,鎖的最長(zhǎng)持有時(shí)間為leaseTime
  • 四、鎖釋放

    看完加鎖邏輯,鎖釋放就更容易理解了。

    直接遍歷釋放鎖即可,lock.unlockAsync() 是調(diào)用的 RedissonBaseLock#unlockAsync() 方法。

    五、使用MultiLock實(shí)現(xiàn)分布式鎖

    • 建立一個(gè)三主三從的redis集群,參考文章

    • 創(chuàng)建springboot項(xiàng)目

    • redissonCluster.yml

    clusterServersConfig:# 連接空閑超時(shí),單位:毫秒 默認(rèn)10000idleConnectionTimeout: 10000pingConnectionInterval: 1000# 同任何節(jié)點(diǎn)建立連接時(shí)的等待超時(shí)。時(shí)間單位是毫秒 默認(rèn)10000connectTimeout: 10000# 等待節(jié)點(diǎn)回復(fù)命令的時(shí)間。該時(shí)間從命令發(fā)送成功時(shí)開始計(jì)時(shí)。默認(rèn)3000timeout: 3000# 命令失敗重試次數(shù)retryAttempts: 3# 命令重試發(fā)送時(shí)間間隔,單位:毫秒retryInterval: 1500# 重新連接時(shí)間間隔,單位:毫秒failedSlaveReconnectionInterval: 3000# 執(zhí)行失敗最大次數(shù)#failedAttempts: 3# 密碼password:# 單個(gè)連接最大訂閱數(shù)量subscriptionsPerConnection: 5clientName: null# loadBalancer 負(fù)載均衡算法類的選擇loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}# 主節(jié)點(diǎn)最小空閑連接數(shù) 默認(rèn)32masterConnectionMinimumIdleSize: 32# 主節(jié)點(diǎn)連接池大小 默認(rèn)64masterConnectionPoolSize: 64# 訂閱操作的負(fù)載均衡模式subscriptionMode: SLAVE# 只在從服務(wù)器讀取readMode: SLAVE# 集群地址nodeAddresses:- "redis://xxx.xxx.xxx.xxx:9001"- "redis://xxx.xxx.xxx.xxx:9002"- "redis://xxx.xxx.xxx.xxx:9003"# 對(duì)Redis集群節(jié)點(diǎn)狀態(tài)掃描的時(shí)間間隔。單位是毫秒。默認(rèn)1000scanInterval: 1000#這個(gè)線程池?cái)?shù)量被所有RTopic對(duì)象監(jiān)聽器,RRemoteService調(diào)用者和RExecutorService任務(wù)共同共享。默認(rèn)2 threads: 0 #這個(gè)線程池?cái)?shù)量是在一個(gè)Redisson實(shí)例內(nèi),被其創(chuàng)建的所有分布式數(shù)據(jù)類型和服務(wù),以及底層客戶端所一同共享的線程池里保存的線程數(shù)量。默認(rèn)2 nettyThreads: 0 # 編碼方式 默認(rèn)org.redisson.codec.JsonJacksonCodec codec: !<org.redisson.codec.JsonJacksonCodec> {} #傳輸模式 transportMode: NIO # 分布式鎖自動(dòng)過(guò)期時(shí)間,防止死鎖,單位毫秒,默認(rèn)30s,每1/3的lockWatchdogTimeout時(shí)間,如果沒執(zhí)行玩業(yè)務(wù),會(huì)自動(dòng)給鎖續(xù)約 lockWatchdogTimeout: 30000 # 通過(guò)該參數(shù)來(lái)修改是否按訂閱發(fā)布消息的接收順序出來(lái)消息,如果選否將對(duì)消息實(shí)行并行處理,該參數(shù)只適用于訂閱發(fā)布消息的情況, 默認(rèn)true keepPubSubOrder: true # 用來(lái)指定高性能引擎的行為。由于該變量值的選用與使用場(chǎng)景息息相關(guān)(NORMAL除外)我們建議對(duì)每個(gè)參數(shù)值都進(jìn)行嘗試。 # #該參數(shù)僅限于Redisson PRO版本。 #performanceMode: HIGHER_THROUGHPUT @Configuration public class RedissonHttpSessionConfig {//服務(wù)停用后調(diào)用shutdown方法@Bean(destroyMethod="shutdown")public RedissonClient getRedissonClient() throws IOException {ResourceLoader loader = new DefaultResourceLoader();Resource resource = loader.getResource("redissonCluster.yml");Config config = Config.fromYAML(resource.getInputStream());return Redisson.create(config);} } @Component public class RedissonMultiLockInit {private final ArrayList<RLock> rLockList=new ArrayList<>();@AutowiredRedissonClient redissonClient;public RedissonMultiLock initLock(String... locksName){for (String lockName : locksName) {rLockList.add(redissonClient.getLock(lockName));}RLock[] rLocks = rLockList.toArray(new RLock[0]);return new RedissonMultiLock(rLocks);}public List<RLock> getRLocks(){return rLockList;} } @Controller @RequestMapping("/lock") public class LockController {@AutowiredRedissonMultiLockInit redissonMultiLockInit;@AutowiredUserMapper userMapper;@AutowiredPlatformTransactionManager transactionManager;@GetMapping("/get/{waitTime}/{leaseTime}")@ResponseBodypublic String getLock(@PathVariable long waitTime, @PathVariable long leaseTime) throws InterruptedException {String[] strings={"test1","test2","test3"};RedissonMultiLock lock = redissonMultiLockInit.initLock(strings);//手動(dòng)開啟事務(wù)管理,@Transitional無(wú)法控制redis的分布式鎖//創(chuàng)建事務(wù)定義對(duì)象DefaultTransactionDefinition def = new DefaultTransactionDefinition();//設(shè)置是否只讀,false支持事務(wù)def.setReadOnly(false);//設(shè)置事務(wù)隔離級(jí)別,可以重復(fù)讀mysql默認(rèn)級(jí)別def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);//設(shè)置事務(wù)傳播行為def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//配置事務(wù)管理器TransactionStatus status = transactionManager.getTransaction(def);if (lock.tryLock(waitTime,leaseTime, TimeUnit.SECONDS)){System.out.println(Thread.currentThread().getName()+" waiting time is "+waitTime+"s " +"leaseTime is "+leaseTime+"s "+"execute time is "+(leaseTime+10)+" s" );try {userMapper.updateById(new User(1L,23,"beijing","myname2"));//模擬執(zhí)行超時(shí)釋放鎖Thread.sleep((leaseTime+10)*1000);List<RLock> rLocks = redissonMultiLockInit.getRLocks();//判斷是否仍然持有所有鎖,防止鎖過(guò)期if(rLocks.stream().allMatch(RLock::isLocked)){//提交業(yè)務(wù)transactionManager.commit(status);//提交業(yè)務(wù)后再釋放分布式鎖lock.unlock();return "unlock success,transition success";}else {//回滾業(yè)務(wù)transactionManager.rollback(status);return "lock is expired,transition fail";}} catch (Exception e) {e.printStackTrace();return "transition error";}}else {return Thread.currentThread().getName()+" can't get the lock,because the waiting time isn't enough. Waiting time is "+waitTime+"s, " +"leaseTime is "+leaseTime+"s ";}} }
    • 注意:這里有一個(gè)很經(jīng)典的@Transitional和分布式鎖同時(shí)使用的問(wèn)題,所以為了解決該問(wèn)題,我們手動(dòng)開啟事務(wù),并確保在事務(wù)提交后,再釋放分布式鎖,關(guān)于這個(gè)問(wèn)題,可以參考這篇文章
    • 測(cè)試
  • http://localhost:8090/lock/get/6/-1,表示最多有6s的等待獲取鎖的時(shí)間,并且業(yè)務(wù)的執(zhí)行時(shí)間可以無(wú)續(xù)約(啟用看門狗機(jī)制),那么這次業(yè)務(wù)是一定會(huì)成功的

  • http://localhost:8090/lock/get/6/9,表示最多有6s的等待獲取鎖的時(shí)間,并且最多有9s的業(yè)務(wù)執(zhí)行時(shí)間,超時(shí)就會(huì)釋放分布式鎖,業(yè)務(wù)失敗,由于我在controller中寫死了業(yè)務(wù)超過(guò)了9s,所以這次業(yè)務(wù)肯定失敗。

  • http://localhost:8090/lock/get/6/-1和http://localhost:8090/lock/get/2/-1

  • 由于第一次業(yè)務(wù)要花費(fèi)9s的業(yè)務(wù)執(zhí)行時(shí)間,那么第二次業(yè)務(wù)無(wú)法在2s的時(shí)間內(nèi)獲取到分布式鎖,會(huì)退出此次業(yè)務(wù)。

    參考文章

    與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的redisson MultiLock原理及分布式锁的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。