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

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

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

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、锁策略的实现

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

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

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

  • 可重入锁:

/**
 * 可重入锁策略
 */
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表达式是数组或者集合 则使用红锁
        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();
    }
}

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

/**
 * 锁的策略工厂
 */
@Service
public class LockStrategyFactory {
    private LockStrategyFactory() {
    }
    private static final Map<LockModel, LockStrategy> 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中如何实现支持几乎所有加锁场景的分布式锁”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/65134.html

(0)

相关推荐

  • 美国服务器搭建游戏服务端有什么好USA-IDC

    技术美国服务器搭建游戏服务端有什么好USA-IDC通过服务器设置,您可以将其视为更像是为其玩家托管视频游戏的远程计算机。由于美国服务器不在游戏引擎上运行,因此它需要使用上述图形卡那样呈现任何内容。然而,它的作用是指示客户

    礼包 2021年12月23日
  • 鼠标连击怎么办教你更换鼠标的微动!

    技术鼠标连击怎么办教你更换鼠标的微动! 鼠标连击怎么办教你更换鼠标的微动!前言
    作为一名合格的程序猿,
    机械键盘+酷炫的鼠标是标配
    键盘鼠标就像女人的包包,
    再穷也得整个好的!
    是作为一个高质量码农最基

    礼包 2021年11月24日
  • java中的堆和栈是什么数据结构(java栈的应用数据结构)

    技术java数据结构中栈怎么应用本篇内容主要讲解“java数据结构中栈怎么应用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java数据结构中栈怎么应用”吧!1.声明一个栈接

    攻略 2021年12月22日
  • C++中怎么使用工厂函数

    技术C++中怎么使用工厂函数本篇内容介绍了“C++中怎么使用工厂函数”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!如果在

    攻略 2021年11月29日
  • xamarin发布ios(xamarin ios移动开发实战)

    技术Xamarin.iOS真机测试报错的示例分析这篇文章将为大家详细讲解有关Xamarin.iOS真机测试报错的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Xamarin.i

    攻略 2021年12月21日
  • 数据结构与算法-二叉树、AVL树、B树、红黑树总结

    技术数据结构与算法-二叉树、AVL树、B树、红黑树总结 数据结构与算法-二叉树、AVL树、B树、红黑树总结转载:原文链接:https://blog.csdn.net/wanderlustLee/arti

    礼包 2021年11月18日