聊聊Redis中怎么實現(xiàn)支持幾乎所有加鎖場景的分布式鎖

本篇文章給大家介紹一下redis分布式鎖的實現(xiàn)方法,希望對大家有所幫助!

聊聊Redis中怎么實現(xiàn)支持幾乎所有加鎖場景的分布式鎖

Hello 大家好,今天給大家分享redisson實現(xiàn)的多類型鎖、支持幾乎所有加鎖場景的redis分布式鎖的實現(xiàn),還支持小型MQ和redis的各種數(shù)據(jù)操作。【相關(guān)推薦:Redis視頻教程

理論部分

在之前的文章中,介紹了通過redis實現(xiàn)分布鎖的兩種方式,分別是:

  • 通過redis自帶的命令:setNX

  • 通過redis的客戶端:redisson

作者更加推薦使用redisson客戶端的方式,因為redisson支持更多的鎖類型,譬如聯(lián)鎖、紅鎖、讀寫鎖、公平鎖等,而且redisson的實現(xiàn)更加簡單,開發(fā)者只需要調(diào)用響應(yīng)的API即可,無需關(guān)心底層加鎖的過程和解鎖的原子性問題。

在Redis分布式鎖中,列出了redisson對于多種的鎖類型的簡單實現(xiàn),即編程式實現(xiàn)。這樣的實現(xiàn)完全能夠滿足我們的日常開發(fā)需求,但是缺點也很明顯。

譬如:

  • 代碼嵌入較多,不夠優(yōu)雅
  • 重復代碼
  • 對鎖的參數(shù)運用不直觀
  • 容易忘掉解鎖的步驟

使用過Spring的同學,肯定都知道@Transactional注解,Spring即支持編程式事務(wù),也支持注解式(聲明式)事務(wù)。

我們是否也可以參考這樣的實現(xiàn)呢?

答案是:完全OK!

AOP就是專門干這種事的。

實戰(zhàn)部分

1、引入redisson依賴

<dependency> ????<groupid>org.redisson</groupid> ????<artifactid>redisson</artifactid> ????<version>3.16.2</version></dependency>Copy?to?clipboardErrorCopied

2、自定義注解

/** ?*?分布式鎖自定義注解 ?*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?Lock?{  ????/** ?????*?鎖的模式:如果不設(shè)置自動模式,當參數(shù)只有一個.使用?REENTRANT?參數(shù)多個?MULTIPLE ?????*/ ????LockModel?lockModel()?default?LockModel.AUTO;  ????/** ?????*?如果keys有多個,如果不設(shè)置,則使用?聯(lián)鎖 ?????* ?????*?@return ?????*/ ????String[]?keys()?default?{};  ????/** ?????*?key的靜態(tài)常量:當key的spel的值是LIST、數(shù)組時使用+號連接將會被spel認為這個變量是個字符串,只能產(chǎn)生一把鎖,達不到我們的目的, ?????*?而我們?nèi)绻中枰粋€常量的話。這個參數(shù)將會在拼接在每個元素的后面 ?????* ?????*?@return ?????*/ ????String?keyConstant()?default?"";  ????/** ?????*?鎖超時時間,默認30000毫秒(可在配置文件全局設(shè)置) ?????* ?????*?@return ?????*/ ????long?watchDogTimeout()?default?30000;  ????/** ?????*?等待加鎖超時時間,默認10000毫秒?-1?則表示一直等待(可在配置文件全局設(shè)置) ?????* ?????*?@return ?????*/ ????long?attemptTimeout()?default?10000; }

3、常量類

/** ?*?Redisson常量類 ?*/ public?class?RedissonConst?{ ????/** ?????*?redisson鎖默認前綴 ?????*/ ????public?static?final?String?REDISSON_LOCK?=?"redisson:lock:"; ????/** ?????*?spel表達式占位符 ?????*/ ????public?static?final?String?PLACE_HOLDER?=?"#"; }

4、枚舉

/** ?*?鎖的模式 ?*/ public?enum?LockModel?{ ????/** ?????*?可重入鎖 ?????*/ ????REENTRANT, ????/** ?????*?公平鎖 ?????*/ ????FAIR, ????/** ?????*?聯(lián)鎖 ?????*/ ????MULTIPLE, ????/** ?????*?紅鎖 ?????*/ ????RED_LOCK, ????/** ?????*?讀鎖 ?????*/ ????READ, ????/** ?????*?寫鎖 ?????*/ ????WRITE, ????/** ?????*?自動模式,當參數(shù)只有一個使用?REENTRANT?參數(shù)多個?RED_LOCK ?????*/ ????AUTO }

5、自定義異常

/** ?*?分布式鎖異常 ?*/ public?class?ReddissonException?extends?RuntimeException?{  ????public?ReddissonException()?{ ????}  ????public?ReddissonException(String?message)?{ ????????super(message); ????}  ????public?ReddissonException(String?message,?Throwable?cause)?{ ????????super(message,?cause); ????}  ????public?ReddissonException(Throwable?cause)?{ ????????super(cause); ????}  ????public?ReddissonException(String?message,?Throwable?cause,?boolean?enableSuppression,?boolean?writableStackTrace)?{ ????????super(message,?cause,?enableSuppression,?writableStackTrace); ????} }

6、AOP切面

???/** ?*?分布式鎖aop ?*/ @Slf4j @Aspect public?class?LockAop?{  ????@Autowired ????private?RedissonClient?redissonClient;  ????@Autowired ????private?RedissonProperties?redissonProperties;  ????@Autowired ????private?LockStrategyFactory?lockStrategyFactory;  ????@Around("@annotation(lock)") ????public?Object?aroundAdvice(ProceedingJoinPoint?proceedingJoinPoint,?Lock?lock)?throws?Throwable?{ ????????//?需要加鎖的key數(shù)組 ????????String[]?keys?=?lock.keys(); ????????if?(ArrayUtil.isEmpty(keys))?{ ????????????throw?new?ReddissonException("redisson?lock?keys不能為空"); ????????} ????????//?獲取方法的參數(shù)名 ????????String[]?parameterNames?=?new?LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature)?proceedingJoinPoint.getSignature()).getMethod()); ????????Object[]?args?=?proceedingJoinPoint.getArgs(); ????????//?等待鎖的超時時間 ????????long?attemptTimeout?=?lock.attemptTimeout(); ????????if?(attemptTimeout?==?0)?{ ????????????attemptTimeout?=?redissonProperties.getAttemptTimeout(); ????????} ????????//?鎖超時時間 ????????long?lockWatchdogTimeout?=?lock.watchdogTimeout(); ????????if?(lockWatchdogTimeout?==?0)?{ ????????????lockWatchdogTimeout?=?redissonProperties.getLockWatchdogTimeout(); ????????} ????????//?加鎖模式 ????????LockModel?lockModel?=?getLockModel(lock,?keys); ????????if?(!lockModel.equals(LockModel.MULTIPLE)?&amp;&amp;?!lockModel.equals(LockModel.RED_LOCK)?&amp;&amp;?keys.length?&gt;?1)?{ ????????????throw?new?ReddissonException("參數(shù)有多個,鎖模式為-&gt;"?+?lockModel.name()?+?",無法匹配加鎖"); ????????} ????????log.info("鎖模式-&gt;{},等待鎖定時間-&gt;{}毫秒,鎖定最長時間-&gt;{}毫秒",?lockModel.name(),?attemptTimeout,?lockWatchdogTimeout); ????????boolean?res?=?false; ????????//?策略模式獲取redisson鎖對象 ????????RLock?rLock?=?lockStrategyFactory.createLock(lockModel,?keys,?parameterNames,?args,?lock.keyConstant(),?redissonClient); ????????//執(zhí)行aop ????????if?(rLock?!=?null)?{ ????????????try?{ ????????????????if?(attemptTimeout?==?-1)?{ ????????????????????res?=?true; ????????????????????//一直等待加鎖 ????????????????????rLock.lock(lockWatchdogTimeout,?TimeUnit.MILLISECONDS); ????????????????}?else?{ ????????????????????res?=?rLock.tryLock(attemptTimeout,?lockWatchdogTimeout,?TimeUnit.MILLISECONDS); ????????????????} ????????????????if?(res)?{ ????????????????????return?proceedingJoinPoint.proceed(); ????????????????}?else?{ ????????????????????throw?new?ReddissonException("獲取鎖失敗"); ????????????????} ????????????}?finally?{ ????????????????if?(res)?{ ????????????????????rLock.unlock(); ????????????????} ????????????} ????????} ????????throw?new?ReddissonException("獲取鎖失敗"); ????}  ????/** ?????*?獲取加鎖模式 ?????* ?????*?@param?lock ?????*?@param?keys ?????*?@return ?????*/ ????private?LockModel?getLockModel(Lock?lock,?String[]?keys)?{ ????????LockModel?lockModel?=?lock.lockModel(); ????????//?自動模式:優(yōu)先匹配全局配置,再判斷用紅鎖還是可重入鎖 ????????if?(lockModel.equals(LockModel.AUTO))?{ ????????????LockModel?globalLockModel?=?redissonProperties.getLockModel(); ????????????if?(globalLockModel?!=?null)?{ ????????????????lockModel?=?globalLockModel; ????????????}?else?if?(keys.length?&gt;?1)?{ ????????????????lockModel?=?LockModel.RED_LOCK; ????????????}?else?{ ????????????????lockModel?=?LockModel.REENTRANT; ????????????} ????????} ????????return?lockModel; ????} }

這里使用了策略模式來對不同的鎖類型提供實現(xiàn)。

7、鎖策略的實現(xiàn)

先定義鎖策略的抽象基類(也可以用接口):

/** ?*?鎖策略抽象基類 ?*/ @Slf4j abstract?class?LockStrategy?{  ????@Autowired ????private?RedissonClient?redissonClient;  ????/** ?????*?創(chuàng)建RLock ?????* ?????*?@param?keys ?????*?@param?parameterNames ?????*?@param?args ?????*?@param?keyConstant ?????*?@return ?????*/ ????abstract?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient);  ????/** ?????*?獲取RLock ?????* ?????*?@param?keys ?????*?@param?parameterNames ?????*?@param?args ?????*?@param?keyConstant ?????*?@return ?????*/ ????public?RLock[]?getRLocks(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant)?{ ????????List<rlock>?rLocks?=?new?ArrayList(); ????????for?(String?key?:?keys)?{ ????????????List<string>?valueBySpel?=?getValueBySpel(key,?parameterNames,?args,?keyConstant); ????????????for?(String?s?:?valueBySpel)?{ ????????????????rLocks.add(redissonClient.getLock(s)); ????????????} ????????} ????????RLock[]?locks?=?new?RLock[rLocks.size()]; ????????int?index?=?0; ????????for?(RLock?r?:?rLocks)?{ ????????????locks[index++]?=?r; ????????} ????????return?locks; ????}  ????/** ?????*?通過spring?Spel?獲取參數(shù) ?????* ?????*?@param?key????????????定義的key值?以#開頭?例如:#user ?????*?@param?parameterNames?形參 ?????*?@param?args???????????形參值 ?????*?@param?keyConstant????key的常亮 ?????*?@return ?????*/ ????List<string>?getValueBySpel(String?key,?String[]?parameterNames,?Object[]?args,?String?keyConstant)?{ ????????List<string>?keys?=?new?ArrayList(); ????????if?(!key.contains(PLACE_HOLDER))?{ ????????????String?s?=?REDISSON_LOCK?+?key?+?keyConstant; ????????????log.info("沒有使用spel表達式value-&gt;{}",?s); ????????????keys.add(s); ????????????return?keys; ????????} ????????//?spel解析器 ????????ExpressionParser?parser?=?new?SpelExpressionParser(); ????????//?spel上下文 ????????EvaluationContext?context?=?new?StandardEvaluationContext(); ????????for?(int?i?=?0;?i?<p>再提供各種鎖模式的具體實現(xiàn):</p> <ul><li><strong>可重入鎖:</strong></li></ul> <pre class="brush:js;toolbar:false;">/** ?*?可重入鎖策略 ?*/ public?class?ReentrantLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????List<string>?valueBySpel?=?getValueBySpel(keys[0],?parameterNames,?args,?keyConstant); ????????//如果spel表達式是數(shù)組或者集合?則使用紅鎖 ????????if?(valueBySpel.size()?==?1)?{ ????????????return?redissonClient.getLock(valueBySpel.get(0)); ????????}?else?{ ????????????RLock[]?locks?=?new?RLock[valueBySpel.size()]; ????????????int?index?=?0; ????????????for?(String?s?:?valueBySpel)?{ ????????????????locks[index++]?=?redissonClient.getLock(s); ????????????} ????????????return?new?RedissonRedLock(locks); ????????} ????} }</string>
  • 公平鎖:
/** ?*?公平鎖策略 ?*/ public?class?FairLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????return?redissonClient.getFairLock(getValueBySpel(keys[0],?parameterNames,?args,?keyConstant).get(0)); ????} }
  • 聯(lián)鎖
/** ?*?聯(lián)鎖策略 ?*/ public?class?MultipleLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????RLock[]?locks?=?getRLocks(keys,?parameterNames,?args,?keyConstant); ????????return?new?RedissonMultiLock(locks); ????} }
  • 紅鎖
/** ?*?紅鎖策略 ?*/ public?class?RedLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????RLock[]?locks?=?getRLocks(keys,?parameterNames,?args,?keyConstant); ????????return?new?RedissonRedLock(locks); ????} }
  • 讀鎖
/** ?*?讀鎖策略 ?*/ public?class?ReadLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????RReadWriteLock?rwLock?=?redissonClient.getReadWriteLock(getValueBySpel(keys[0],?parameterNames,?args,?keyConstant).get(0)); ????????return?rwLock.readLock(); ????} }
  • 寫鎖
/** ?*?寫鎖策略 ?*/ public?class?WriteLockStrategy?extends?LockStrategy?{  ????@Override ????public?RLock?createLock(String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????RReadWriteLock?rwLock?=?redissonClient.getReadWriteLock(getValueBySpel(keys[0],?parameterNames,?args,?keyConstant).get(0)); ????????return?rwLock.writeLock(); ????} }

最后提供一個策略工廠初始化鎖策略:

/** ?*?鎖的策略工廠 ?*/ @Service public?class?LockStrategyFactory?{  ????private?LockStrategyFactory()?{ ????}  ????private?static?final?Map<lockmodel>?STRATEGIES?=?new?HashMap(6);  ????static?{ ????????STRATEGIES.put(LockModel.FAIR,?new?FairLockStrategy()); ????????STRATEGIES.put(LockModel.REENTRANT,?new?ReentrantLockStrategy()); ????????STRATEGIES.put(LockModel.RED_LOCK,?new?RedLockStrategy()); ????????STRATEGIES.put(LockModel.READ,?new?ReadLockStrategy()); ????????STRATEGIES.put(LockModel.WRITE,?new?WriteLockStrategy()); ????????STRATEGIES.put(LockModel.MULTIPLE,?new?MultipleLockStrategy()); ????}  ????public?RLock?createLock(LockModel?lockModel,?String[]?keys,?String[]?parameterNames,?Object[]?args,?String?keyConstant,?RedissonClient?redissonClient)?{ ????????return?STRATEGIES.get(lockModel).createLock(keys,?parameterNames,?args,?keyConstant,?redissonClient); ????} }</lockmodel>

8、使用方式

????@Lock(keys?=?"#query.channel")?//?支持spel ????@ApiOperation("分頁列表") ????@GetMapping ????public?ApiPageResult?list(VendorProjectItemQuery?query,?Pagination?pagination)?{ ????????return?ApiPageResult.success(pagination,?vendorProjectItemService.list(query,?pagination),?vendorProjectItemService.count(query)); ????}

大功告成,順利吃雞聊聊Redis中怎么實現(xiàn)支持幾乎所有加鎖場景的分布式鎖聊聊Redis中怎么實現(xiàn)支持幾乎所有加鎖場景的分布式鎖聊聊Redis中怎么實現(xiàn)支持幾乎所有加鎖場景的分布式鎖

更多編程相關(guān)知識,請訪問:Redis視頻教程!!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊15 分享