如何使用注解优雅地实现分布式锁Redssion

4 篇文章 0 订阅

如何使用注解实现Redssion分布式锁

  1. 简介
    在分布式系统的并发场景中,存在多个进程或线程对共享资源的并发访问,为了保证数据的一致性往往需要使用分布式锁来解决这个问题。

  2. 认识分布式锁Redssion
    Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格客户端(In-Memory Data Grid)。它不仅提供了一系列的 redis 常用数据结构命令服务,还提供了许多分布式服务,例如分布式锁、分布式对象、分布式集合、分布式远程服务、分布式调度任务服务等等。

目前业界实现分布式锁的方式多种多样,如手动加锁解锁 、 分布式锁工具类 、 注解版分布式锁,注解版分布式锁时最三者中最上层的抽象,下面将介绍Reddsion注解版的实现逻辑。

  1. 引入依赖
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.25.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
  1. 实现分布式锁逻辑
  • Redssion配置
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.setTransportMode(TransportMode.NIO);
        SingleServerConfig singleServerConfig = config.useSingleServer();
        //可以用"rediss://"来启用SSL连接
        singleServerConfig.setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
  • 定义分布式锁注解,用来锁对应的方法逻辑
	@Documented
	// 注解是否可以继承
	@Inherited
	// 指定注解在运行时保留
	@Retention(RetentionPolicy.RUNTIME)
	// 表示注解只能应用于方法上
	@Target({ElementType.METHOD})
	public @interface RedssionLockAspect {
	  
		/**
		  * 分布式锁key
		  * 支持SpEL 表达式
		  */
		 String key() default "";
		 /**
		  * 最大等待时间 时间单位以 unit为准
		  * 默认值(-1) 不等待
		  */
		 int waitTime() default -1;
		
		 /**
		  * 加锁多久后自动解锁 时间单位以 unit为准
		  * 默认值(-1):默认30s,会通过watchDog机制自动续期
		  * 其它值,值过期后会自动解锁,不会自动续期
		  */
		 int leaseTime() default -1;
		 /**
		  * 时间单位,默认s
		  */
		 TimeUnit unit() default TimeUnit.SECONDS;
	}
  • 编写切面(分布式锁key支持spel表达式)
@Component
@Aspect
@Order(0)
@Slf4j
public class RedssionLockHandler {

    @Autowired
    private RedissonClient redissonClient;

    // AOP 处理分布式锁逻辑
    @Around("@annotation(redssionLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedssionLockAspect redssionLock) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String key = redssionLock.key();
        String redssionKey = getRedssionKey(joinPoint, method, key);

        Object obj = null;
        int waitTime = redssionLock.waitTime();
        int leaseTime = redssionLock.leaseTime();
        TimeUnit timeUnit = redssionLock.unit();
        RLock lock = redissonClient.getLock(redssionKey);

        boolean lockFlag = leaseTime != -1 ? lock.tryLock(waitTime, leaseTime, timeUnit) :
                waitTime != -1 ? lock.tryLock(waitTime, timeUnit) : lock.tryLock();

        if (!lockFlag) {
            log.info("redssionKey{},获取分布式锁失败", redssionKey);
            throw new RuntimeException("分布式锁正在处理中");
        }

        try {
            obj = joinPoint.proceed();
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        } finally {
            /*
             * isHeldByCurrentThread方法 和 isLocked方法的区别
             * isHeldByCurrentThread: 用于检查当前线程是否持有锁
             * isLocked: 用于检查锁是否被任何线程持有
             */
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return obj;
    }

    /**
     * 获取redis key
     *
     * @param joinPoint
     * @param signature
     * @param method
     * @param key
     * @return
     */
    private String getRedssionKey(ProceedingJoinPoint joinPoint, Method method, String key) {
        return StringUtils.isNotBlank(key) ? spelParseRedssionKey(joinPoint, method, key) : getMethodKey(method);
    }

    /**
     * 类名加方法名
     * @param method
     * @return
     */
    private String getMethodKey(Method method) {
        return method.getDeclaringClass() + "-" + method.getName();
    }

    /**
     * spel表达式解析key
     *
     * @param joinPoint
     * @param method
     * @param key
     * @return
     */
    private String spelParseRedssionKey(ProceedingJoinPoint joinPoint, Method method, String key) {
        Object[] args = joinPoint.getArgs();
        ExpressionParser spelExpressionParser = new SpelExpressionParser();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

        String[] paramNames = discoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }

        Expression expression = spelExpressionParser.parseExpression(key);
        return expression.getValue(context, String.class);
    }

} 
  • 测试
    @RedssionLockAspect(key = "'testRedssion'+#user.name")
    public void testDistributeLock(User user) {
        System.out.println("id--->" + user.toString());
    } 

通过上面的步骤,一个支持Spel表达式注解版的分布式锁就实现啦!!!源码地址:https://gitee.com/git_xiaodong/springboot-demo,供参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值