千家信息网

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

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,小编给大家分享一下Redis中如何实现支持几乎所有加锁场景的分布式锁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!实战部分
千家信息网最后更新 2025年01月19日Redis中如何实现支持几乎所有加锁场景的分布式锁

小编给大家分享一下Redis中如何实现支持几乎所有加锁场景的分布式锁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

实战部分

1、引入redisson依赖

    org.redisson    redisson    3.16.2Copy to clipboardErrorCopied

2、自定义注解

/** * 分布式锁自定义注解 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Lock {    /**     * 锁的模式:如果不设置自动模式,当参数只有一个.使用 REENTRANT 参数多个 MULTIPLE     */    LockModel lockModel() default LockModel.AUTO;    /**     * 如果keys有多个,如果不设置,则使用 联锁     *     * @return     */    String[] keys() default {};    /**     * key的静态常量:当key的spel的值是LIST、数组时使用+号连接将会被spel认为这个变量是个字符串,只能产生一把锁,达不到我们的目的,     * 而我们如果又需要一个常量的话。这个参数将会在拼接在每个元素的后面     *     * @return     */    String keyConstant() default "";    /**     * 锁超时时间,默认30000毫秒(可在配置文件全局设置)     *     * @return     */    long watchDogTimeout() default 30000;    /**     * 等待加锁超时时间,默认10000毫秒 -1 则表示一直等待(可在配置文件全局设置)     *     * @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,    /**     * 联锁     */    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@Aspectpublic 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、锁策略的实现

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

/** * 锁策略抽象基类 */@Slf4jabstract 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 rLocks = new ArrayList<>();        for (String key : keys) {            List 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 getValueBySpel(String key, String[] parameterNames, Object[] args, String keyConstant) {        List 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.parse_Expression(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;    }}

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

  • 可重入锁:

/** * 可重入锁策略 */public class ReentrantLockStrategy extends LockStrategy {    @Override    public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {        List valueBySpel = getValueBySpel(keys[0], parameterNames, args, keyConstant);        //如果spel表达式是数组或者集合 则使用红锁        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);        }    }}
  • 公平锁:

/** * 公平锁策略 */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));    }}
  • 联锁

/** * 联锁策略 */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();    }}

最后提供一个策略工厂初始化锁策略:

/** * 锁的策略工厂 */@Servicepublic class LockStrategyFactory {    private LockStrategyFactory() {    }    private static final Map 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);    }}

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中如何实现支持几乎所有加锁场景的分布式锁"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0