聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁

本篇文章给大家介绍一下redis分布式锁的实现方法,希望对大家有所帮助!

聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁

Hello 大家好,今天给大家分享redisson实现的多类型锁、支持几乎所有加锁场景的redis分布式锁的实现,还支持小型MQ和redis的各种数据操作。【相关推荐:Redis视频教程】

理论部分

在之前的文章中,介绍了通过redis实现分布锁的两种方式,分别是:

通过redis自带的命令:setNX

通过redis的客户端:redisson

作者更加推荐使用redisson客户端的方式,因为redisson支持更多的锁类型,譬如联锁、红锁、读写锁、公平锁等,而且redisson的实现更加简单,开发者只需要调用响应的API即可,无需关心底层加锁的过程和解锁的原子性问题。

在Redis分布式锁中,列出了redisson对于多种的锁类型的简单实现,即编程式实现。这样的实现完全能够满足我们的日常开发需求,但是缺点也很明显。

譬如:

代码嵌入较多,不够优雅重复代码对锁的参数运用不直观容易忘掉解锁的步骤

使用过Spring的同学,肯定都知道@Transactional注解,Spring即支持编程式事务,也支持注解式(声明式)事务。

我们是否也可以参考这样的实现呢?

答案是:完全OK!

AOP就是专门干这种事的。

实战部分

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 

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

  • 可重入锁:
/** * 可重入锁策略 */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中怎么实现支持几乎所有加锁场景的分布式锁聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁

更多编程相关知识,请访问:编程视频!!

以上就是聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2039905.html

(0)
上一篇 2025年2月24日 00:06:48
下一篇 2025年2月18日 02:32:27

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 一文聊聊Redis中的epoll和文件事件

    本篇文章给大家介绍一下redis中的文件事件,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 事件驱动 Redis 服务器是事件驱动程序,分为文件事件和时间事件 文件事件:socket 的可读可写事件定时任务 【相关推荐:R…

    2025年2月24日
    200
  • 总结分享几款实用Redis可视化工具

    本篇文章总结了几款实用redis可视化工具分享给大家,希望对大家有所帮助,快来收藏吧! 不啰嗦,我们直接开始! 1、命令行 1.1、iredis 利用iredis,用|将redis通过pipe用shell的其他工具,比如jq/fx/rg/s…

    2025年2月24日 数据库
    200
  • 浅谈Redis中的字典、哈希算法和ReHash原理

    本篇文章带大家了解一下redis中的字典、哈希算法和rehash原理,希望对大家有所帮助! Redis 中的字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。 字典的底层实现为哈希表,每个字典带有两个哈希表,一个平时使用,另一…

    2025年2月24日
    200
  • 浅析Redis缓存中的8种淘汰策略

    本篇文章带大家来聊聊redis缓存中的8种淘汰策略,看看应该怎么使用它们,希望对大家有所帮助! 我们知道Redis缓存使用内存来保存数据,但内存大小毕竟有限,随着要缓存的数据量越来越大,有限的缓存空间不可避免地会被写满。这时候就需要缓存的淘…

    2025年2月24日
    200
  • Redis怎么进行去重?4种去重方法浅析

    redis怎么进行去重?下面本篇文章给大家介绍一下redis去重的4种方法,希望对大家有所帮助! 这篇文章主要介绍了Redis实现唯一计数的3种方法分享,本文讲解了基于SET、基于 bit、基于 HyperLogLog三种方法,需要的朋友可…

    2025年2月24日
    200
  • 浅析Redis中AOF的原理和缺点

    本篇文章带大家了解一下redis持久化中的aof,介绍一下aof原理、aof缺点,希望对大家有所帮助! AOF 上文我们提到了rRedis中的一种持久化方式就是RDB 此文我们来讲另一种实现方式那就是AOF AOF是通过只记录Redis写入…

    2025年2月24日
    200
  • redis中分布式session不一致性怎么办

    分布式session不一致性怎么办?下面本篇文章给大家介绍一下redis中分布式session不一致性的解决方案,希望对大家有所帮助! 分布式session不一致性解决方案 一、Session有什么作用? Session 是客户端与服务器通…

    2025年2月24日 数据库
    200
  • Redis中什么是慢查询、订阅模式

    本篇文章给大家介绍一下redis中的慢查询和订阅模式,希望对大家有所帮助! 慢查询 慢查询日志就是系统在命令执行时每条命令的执行时间,当超过阀值,就将这条命令记录下来。【相关推荐:Redis视频教程】 Redis命令执行流程 发送命令 命令…

    2025年2月24日 数据库
    200
  • 深入了解Redis中的主从同步机制

    本篇文章带大家了解一下redis中的主从同步,介绍一下redis主从的两种结构模型、主从关系的建立、主从复制策略等,希望对大家有所帮助! 之前的文章中详细分析了redis的特性和核心原理,从本篇开始将对redis的部署结构和运行模式进行分析…

    2025年2月24日 数据库
    200
  • 浅析Python中怎么用Redis

    python中怎么用redis?下面本篇文章给大家介绍一下python使用redis的方法,希望对大家有所帮助! 前面我们都是使用 Redis 客户端对 Redis 进行使用的,但是实际工作中,我们大多数情况下都是通过代码来使用 Redis…

    2025年2月24日 数据库
    200

发表回复

登录后才能评论