09月22日, 2014 150次
边肖想和大家分享一下如何在Redis中实现支持几乎所有锁场景的分布式锁。相信大部分人还是不太了解。因此,我想分享这篇文章供你参考。希望你看完这篇文章后收获多多。让我们一起来看看。
groupIdorg.redisson/groupId
artifactidredison/artifactId
版本3 . 16 . 2/版本
/dependency copytoclipborderrorcopy
/* * *分布式锁自定义注释。 @Target(ElementType。方法) @Retention(保留策略。RUNTIME) public@interfaceLock{ *锁定模式:如果没有设置自动模式,当只有一个参数时,使用MULTIPLE REENTRANT参数。 lockmodelockmodel(). defaultLockModel。自动; *如果有多个键,如果没有设置,则使用互锁。 * @返回 string[]key()默认为{ }; * key的静态常数:当key的spel的值为LIST或array时,使用数字连接会让spel认为这个变量是字符串,只能产生一个锁,不能达到我们的目的。 *如果我们需要一个常数。此参数将在每个元素后拼接。 * @返回 StringkeyConstant()(默认值“”; *锁定超时,默认为30000毫秒(可在配置文件中全局设置)。 * @返回 longwatchDogTimeout()默认值30000; *等待锁定超时,默认值为10000毫秒-1表示一直等待(可以在配置文件中全局设置)。 * @返回 long temperatimeout()default 10000; }
1、引入redisson依赖/** * Redisson常量类 */ public class RedissonConst { /** * redisson锁默认前缀 */ public static final String REDISSON_LOCK = redisson:lock: /** * spel表达式占位符 */ public static final String PLACE_HOLDER = # }/** * 锁的模式 */ public enum LockModel { /** * 可重入锁 */ REENTRANT, /** * 公平锁 */ FAIR, /** * 联锁 */ MULTIPLE, /** * 红锁 */ RED_LOCK, /** * 读锁 */ READ, /** * 写锁 */ WRITE, /** * 自动模式,当参数只有一个使用 REENTRANT 参数多个 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数组 String[] keys = lock.keys(); if (ArrayUtil.isEmpty(keys)) { throw new ReddissonException( redisson lock keys不能为空 } // 获取方法的参数名 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) !lockModel.equals(LockModel.RED_LOCK) keys.length 1) { throw new ReddissonException( 参数有多个,锁模式为- + lockModel.name() + ,无法匹配加锁 } log.info( 锁模式- {},等待锁定时间- {}毫秒,锁定最长时间- {}毫秒 , lockModel.name(), attemptTimeout, lockWatchdogTimeout); boolean res = false; // 策略模式获取redisson锁对象 RLock rLock = lockStrategyFactory.createLock(lockModel, keys, parameterNames, args, lock.keyConstant(), redissonClient); //执行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(); // 自动模式:优先匹配全局配置,再判断用红锁还是可重入锁 if (lockModel.equals(LockModel.AUTO)) { LockModel globalLockModel = redissonProperties.getLockModel(); if (globalLockModel != null) { lockModel = globalLockModel; } else if (keys.length 1) { lockModel = LockModel.RED_LOCK; } else { lockModel = LockModel.REENTRANT; } } return lockModel; } }这里使用了策略模式来对不同的锁类型提供实现。
7、锁策略的实现先定义锁策略的抽象基类(也可以用接口):
/** * 锁策略抽象基类 */ @Slf4j abstract class LockStrategy { @Autowired private RedissonClient redissonClient; /** * 创建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 获取参数 * * @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- {} , s); keys.add(s); return keys; } // spel解析器 ExpressionParser parser = new SpelExpressionParser(); // spel上下文 EvaluationContext context = new StandardEvaluationContext(); for (int i = 0; i parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i]); } Expression expression = parser.parseExpression(key); Object value = expression.getValue(context); if (value != null) { if (value instanceof List) { List valueList = (List) value; for (Object o : valueList) { keys.add(REDISSON_LOCK + o.toString() + keyConstant); } } else if (value.getClass().isArray()) { Object[] objects = (Object[]) value; for (Object o : objects) { keys.add(REDISSON_LOCK + o.toString() + keyConstant); } } else { keys.add(REDISSON_LOCK + value.toString() + keyConstant); } } log.info( spel表达式key={},value={} , key, keys); return keys; } }再提供各种锁模式的具体实现: