边肖想和大家分享一下如何在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;
 }
}

这里使用了策略模式来对不同的锁类型提供实现。

Redis中如何实现支持几乎所有加锁场景的分布式锁 第1张

7、锁策略的实现

先定义锁策略的抽象基类(也可以用接口):

Redis中如何实现支持几乎所有加锁场景的分布式锁 第2张

/**
 * 锁策略抽象基类
 */
@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;
 }
}

再提供各种锁模式的具体实现: