Redis中如何实现支持几乎所有加锁场景的分布式锁
小编给大家分享一下Redis中如何实现支持几乎所有加锁场景的分布式锁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
实战部分1、引入redisson依赖<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.2</version></dependency>CopytoclipboardErrorCopied2、自定义注解
/***分布式锁自定义注解*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceLock{/***锁的模式:如果不设置自动模式,当参数只有一个.使用REENTRANT参数多个MULTIPLE*/LockModellockModel()defaultLockModel.AUTO;/***如果keys有多个,如果不设置,则使用联锁**@return*/String[]keys()default{};/***key的静态常量:当key的spel的值是LIST、数组时使用+号连接将会被spel认为这个变量是个字符串,只能产生一把锁,达不到我们的目的,*而我们如果又需要一个常量的话。这个参数将会在拼接在每个元素的后面**@return*/StringkeyConstant()default"";/***锁超时时间,默认30000毫秒(可在配置文件全局设置)**@return*/longwatchDogTimeout()default30000;/***等待加锁超时时间,默认10000毫秒-1则表示一直等待(可在配置文件全局设置)**@return*/longattemptTimeout()default10000;}3、常量类
/***Redisson常量类*/publicclassRedissonConst{/***redisson锁默认前缀*/publicstaticfinalStringREDISSON_LOCK="redisson:lock:";/***spel表达式占位符*/publicstaticfinalStringPLACE_HOLDER="#";}4、枚举
/***锁的模式*/publicenumLockModel{/***可重入锁*/REENTRANT,/***公平锁*/FAIR,/***联锁*/MULTIPLE,/***红锁*/RED_LOCK,/***读锁*/READ,/***写锁*/WRITE,/***自动模式,当参数只有一个使用REENTRANT参数多个RED_LOCK*/AUTO}5、自定义异常
/***分布式锁异常*/publicclassReddissonExceptionextendsRuntimeException{publicReddissonException(){}publicReddissonException(Stringmessage){super(message);}publicReddissonException(Stringmessage,Throwablecause){super(message,cause);}publicReddissonException(Throwablecause){super(cause);}publicReddissonException(Stringmessage,Throwablecause,booleanenableSuppression,booleanwritableStackTrace){super(message,cause,enableSuppression,writableStackTrace);}}6、AOP切面
/***分布式锁aop*/@Slf4j@AspectpublicclassLockAop{@AutowiredprivateRedissonClientredissonClient;@AutowiredprivateRedissonPropertiesredissonProperties;@AutowiredprivateLockStrategyFactorylockStrategyFactory;@Around("@annotation(lock)")publicObjectaroundAdvice(ProceedingJoinPointproceedingJoinPoint,Locklock)throwsThrowable{//需要加锁的key数组String[]keys=lock.keys();if(ArrayUtil.isEmpty(keys)){thrownewReddissonException("redissonlockkeys不能为空");}//获取方法的参数名String[]parameterNames=newLocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature)proceedingJoinPoint.getSignature()).getMethod());Object[]args=proceedingJoinPoint.getArgs();//等待锁的超时时间longattemptTimeout=lock.attemptTimeout();if(attemptTimeout==0){attemptTimeout=redissonProperties.getAttemptTimeout();}//锁超时时间longlockWatchdogTimeout=lock.watchdogTimeout();if(lockWatchdogTimeout==0){lockWatchdogTimeout=redissonProperties.getLockWatchdogTimeout();}//加锁模式LockModellockModel=getLockModel(lock,keys);if(!lockModel.equals(LockModel.MULTIPLE)&&!lockModel.equals(LockModel.RED_LOCK)&&keys.length>1){thrownewReddissonException("参数有多个,锁模式为->"+lockModel.name()+",无法匹配加锁");}log.info("锁模式->{},等待锁定时间->{}毫秒,锁定最长时间->{}毫秒",lockModel.name(),attemptTimeout,lockWatchdogTimeout);booleanres=false;//策略模式获取redisson锁对象RLockrLock=lockStrategyFactory.createLock(lockModel,keys,parameterNames,args,lock.keyConstant(),redissonClient);//执行aopif(rLock!=null){try{if(attemptTimeout==-1){res=true;//一直等待加锁rLock.lock(lockWatchdogTimeout,TimeUnit.MILLISECONDS);}else{res=rLock.tryLock(attemptTimeout,lockWatchdogTimeout,TimeUnit.MILLISECONDS);}if(res){returnproceedingJoinPoint.proceed();}else{thrownewReddissonException("获取锁失败");}}finally{if(res){rLock.unlock();}}}thrownewReddissonException("获取锁失败");}/***获取加锁模式**@paramlock*@paramkeys*@return*/privateLockModelgetLockModel(Locklock,String[]keys){LockModellockModel=lock.lockModel();//自动模式:优先匹配全局配置,再判断用红锁还是可重入锁if(lockModel.equals(LockModel.AUTO)){LockModelglobalLockModel=redissonProperties.getLockModel();if(globalLockModel!=null){lockModel=globalLockModel;}elseif(keys.length>1){lockModel=LockModel.RED_LOCK;}else{lockModel=LockModel.REENTRANT;}}returnlockModel;}}
这里使用了策略模式
来对不同的锁类型提供实现。
先定义锁策略的抽象基类(也可以用接口):
/***锁策略抽象基类*/@Slf4jabstractclassLockStrategy{@AutowiredprivateRedissonClientredissonClient;/***创建RLock**@paramkeys*@paramparameterNames*@paramargs*@paramkeyConstant*@return*/abstractRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient);/***获取RLock**@paramkeys*@paramparameterNames*@paramargs*@paramkeyConstant*@return*/publicRLock[]getRLocks(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant){List<RLock>rLocks=newArrayList<>();for(Stringkey:keys){List<String>valueBySpel=getValueBySpel(key,parameterNames,args,keyConstant);for(Strings:valueBySpel){rLocks.add(redissonClient.getLock(s));}}RLock[]locks=newRLock[rLocks.size()];intindex=0;for(RLockr:rLocks){locks[index++]=r;}returnlocks;}/***通过springSpel获取参数**@paramkey定义的key值以#开头例如:#user*@paramparameterNames形参*@paramargs形参值*@paramkeyConstantkey的常亮*@return*/List<String>getValueBySpel(Stringkey,String[]parameterNames,Object[]args,StringkeyConstant){List<String>keys=newArrayList<>();if(!key.contains(PLACE_HOLDER)){Strings=REDISSON_LOCK+key+keyConstant;log.info("没有使用spel表达式value->{}",s);keys.add(s);returnkeys;}//spel解析器ExpressionParserparser=newSpelExpressionParser();//spel上下文EvaluationContextcontext=newStandardEvaluationContext();for(inti=0;i<parameterNames.length;i++){context.setVariable(parameterNames[i],args[i]);}Expressionexpression=parser.parseExpression(key);Objectvalue=expression.getValue(context);if(value!=null){if(valueinstanceofList){ListvalueList=(List)value;for(Objecto:valueList){keys.add(REDISSON_LOCK+o.toString()+keyConstant);}}elseif(value.getClass().isArray()){Object[]objects=(Object[])value;for(Objecto:objects){keys.add(REDISSON_LOCK+o.toString()+keyConstant);}}else{keys.add(REDISSON_LOCK+value.toString()+keyConstant);}}log.info("spel表达式key={},value={}",key,keys);returnkeys;}}
再提供各种锁模式的具体实现:
可重入锁:
/***可重入锁策略*/publicclassReentrantLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){List<String>valueBySpel=getValueBySpel(keys[0],parameterNames,args,keyConstant);//如果spel表达式是数组或者集合则使用红锁if(valueBySpel.size()==1){returnredissonClient.getLock(valueBySpel.get(0));}else{RLock[]locks=newRLock[valueBySpel.size()];intindex=0;for(Strings:valueBySpel){locks[index++]=redissonClient.getLock(s);}returnnewRedissonRedLock(locks);}}}
公平锁:
/***公平锁策略*/publicclassFairLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){returnredissonClient.getFairLock(getValueBySpel(keys[0],parameterNames,args,keyConstant).get(0));}}
联锁
/***联锁策略*/publicclassMultipleLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){RLock[]locks=getRLocks(keys,parameterNames,args,keyConstant);returnnewRedissonMultiLock(locks);}}
红锁
/***红锁策略*/publicclassRedLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){RLock[]locks=getRLocks(keys,parameterNames,args,keyConstant);returnnewRedissonRedLock(locks);}}
读锁
/***读锁策略*/publicclassReadLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){RReadWriteLockrwLock=redissonClient.getReadWriteLock(getValueBySpel(keys[0],parameterNames,args,keyConstant).get(0));returnrwLock.readLock();}}
写锁
/***写锁策略*/publicclassWriteLockStrategyextendsLockStrategy{@OverridepublicRLockcreateLock(String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){RReadWriteLockrwLock=redissonClient.getReadWriteLock(getValueBySpel(keys[0],parameterNames,args,keyConstant).get(0));returnrwLock.writeLock();}}
最后提供一个策略工厂初始化锁策略:
/***锁的策略工厂*/@ServicepublicclassLockStrategyFactory{privateLockStrategyFactory(){}privatestaticfinalMap<LockModel,LockStrategy>STRATEGIES=newHashMap<>(6);static{STRATEGIES.put(LockModel.FAIR,newFairLockStrategy());STRATEGIES.put(LockModel.REENTRANT,newReentrantLockStrategy());STRATEGIES.put(LockModel.RED_LOCK,newRedLockStrategy());STRATEGIES.put(LockModel.READ,newReadLockStrategy());STRATEGIES.put(LockModel.WRITE,newWriteLockStrategy());STRATEGIES.put(LockModel.MULTIPLE,newMultipleLockStrategy());}publicRLockcreateLock(LockModellockModel,String[]keys,String[]parameterNames,Object[]args,StringkeyConstant,RedissonClientredissonClient){returnSTRATEGIES.get(lockModel).createLock(keys,parameterNames,args,keyConstant,redissonClient);}}8、使用方式
@Lock(keys="#query.channel")//支持spel@ApiOperation("分页列表")@GetMappingpublicApiPageResultlist(VendorProjectItemQueryquery,Paginationpagination){returnApiPageResult.success(pagination,vendorProjectItemService.list(query,pagination),vendorProjectItemService.count(query));}
以上是“Redis中如何实现支持几乎所有加锁场景的分布式锁”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。