生活随笔
收集整理的這篇文章主要介紹了
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)加鎖,直到所有的都成功。
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)分布式鎖
clusterServersConfig : idleConnectionTimeout : 10000 pingConnectionInterval : 1000 connectTimeout : 10000 timeout : 3000 retryAttempts : 3 retryInterval : 1500 failedSlaveReconnectionInterval : 3000 password : subscriptionsPerConnection : 5 clientName : null loadBalancer : !<org.redisson.connection.balancer.RoundRobinLoadBalancer> { } masterConnectionMinimumIdleSize : 32 masterConnectionPoolSize : 64 subscriptionMode : SLAVE
readMode : SLAVE
nodeAddresses : - "redis://xxx.xxx.xxx.xxx:9001" - "redis://xxx.xxx.xxx.xxx:9002" - "redis://xxx.xxx.xxx.xxx:9003" scanInterval : 1000
threads : 0
nettyThreads : 0
codec : !<org.redisson.codec.JsonJacksonCodec> { }
transportMode : NIO
lockWatchdogTimeout : 30000
keepPubSubOrder : true
@Configuration
public class RedissonHttpSessionConfig { @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 < > ( ) ; @Autowired RedissonClient 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 { @Autowired RedissonMultiLockInit redissonMultiLockInit
; @Autowired UserMapper userMapper
; @Autowired PlatformTransactionManager transactionManager
; @GetMapping ( "/get/{waitTime}/{leaseTime}" ) @ResponseBody public String getLock ( @PathVariable long waitTime
, @PathVariable long leaseTime
) throws InterruptedException { String [ ] strings
= { "test1" , "test2" , "test3" } ; RedissonMultiLock lock
= redissonMultiLockInit
. initLock ( strings
) ; DefaultTransactionDefinition def
= new DefaultTransactionDefinition ( ) ; def
. setReadOnly ( false ) ; def
. setIsolationLevel ( TransactionDefinition . ISOLATION_REPEATABLE_READ
) ; def
. setPropagationBehavior ( TransactionDefinition . PROPAGATION_REQUIRED
) ; 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" ) ) ; Thread . sleep ( ( leaseTime
+ 10 ) * 1000 ) ; List < RLock > rLocks
= redissonMultiLockInit
. getRLocks ( ) ; if ( rLocks
. stream ( ) . allMatch ( RLock :: isLocked ) ) { transactionManager
. commit ( status
) ; lock
. unlock ( ) ; return "unlock success,transition success" ; } else { 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ò),歡迎將生活随笔 推薦給好友。