关于java:如何使用注解实现分布式锁

5次阅读

共计 2053 个字符,预计需要花费 6 分钟才能阅读完成。

前言

分布式锁想必大家并不生疏:管制分布式系统之间同步访问共享资源的一种形式。

如果不同的零碎或是同一个零碎的不同主机之间共享了一个或一组资源,那么拜访这些资源的时候,往往须要互斥来避免彼此烦扰来保障一致性,在这种状况下,便须要应用到分布式锁。

实现分布式锁的形式多种多样,但一般来说都是应用编码的形式在业务代码中交叉加锁解锁逻辑。

这种形式长处是非常模版化,简直不会出错,每一套加锁解锁逻辑都是截然不同。

毛病也是这个,虽说 ctrl c, ctrl v 很爽,但始终 ctrl c, ctrl v 也会让人感到痛苦。

所以,如何基于该思维实现一把注解版的分布式锁呢?

实现目标

业务举例:下单功能为避免用户反复下单,短时间内只容许用户一次点击胜利。

@RedisLock(name = "order", keys = {"#userId"})
public String order(Long userId){return "ok";}

如代码所示:只需在办法上加上注解,即可实现分布式锁。

name: 锁的名称

keys: 该业务唯一性资源标识,比方这里是短时间内某个用户只能下一次单,所以 keys 为用户 id

name + keys 组合成 redis 中的 key

设计

大多数由注解设计的性能都是通过切面实现的,这个也不例外。

最简略的实现形式就是将原始代码的加锁解锁逻辑拷贝到切面中实现。

如原始代码如下:

@Resource
private RedissonClient redissonClient;

public String order(Long userId){
  String key = "order" + userId;
  RLock lock = redissonClient.getLock(key);
  try {
    // 尝试加锁,true 示意加锁胜利
    if (lock.tryLock()) {
      // 业务代码
      return "ok";
    }
    return "fail";
  }finally {
    // 是否为该线程持有锁
    if (lock.isHeldByCurrentThread()) {lock.unlock();
    }
  }
}

显然,除了 return ok 这行代码,其余的都是模板代码。

redissonClient: redisson 框架的客户端,官网文档:https://github.com/redisson/r…

AOP 切面设计如下:

编码

1、定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
    /**
     * key 的名称前缀,与 keys 字段独特拼接出一个 redis key
     * 如 name = user keys = 123(userId)* 最终应用的 key 为 user123
     */
    String name();

    /**
     * 可能确定出零碎中唯一性资源的 key
     * 如 用户,应用用户 id 为 key
     * 或者应用 用户名 + 手机号
     * 必须为 spel 表达式 如 #id #user.id #user.name
     */
    String[] keys();
}

2、编写切面

@Aspect
public class RedisLockAspect {

    @Resource
    private RedissonClient redissonClient;

    @Around(value = "@annotation(redisLock)")
    public Object lock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        // 拼接出 key 应用 spel 解析注解中定义的 key
        String key = getRedisKey(joinPoint, redisLock);
        // 获取锁
        RLock lock = redissonClient.getLock(key);
        try {
            // 加锁
            if (lock.tryLock()) {return joinPoint.proceed();
            }
            throw new RuntimeException("加锁失败");
        }finally {if (lock.isHeldByCurrentThread()) {lock.unlock();
            }
        }
    }
}

一个超级简略的注解版 Redis 锁就实现了

读者:“啪!你就写个这破玩意搪塞我呢!”

“别别别,我错了,你听我持续说呀!“

组件库

以上内容只是流传思维,对,思维。

好用的注解版 Redis 锁我曾经实现了。并且曾经将它公布到了 Maven 地方仓库。

对于它的具体文档还请各位移步:https://github.com/lzj960515/…

如果你想晓得其中的实现,欢送 clone 代码浏览或者与我交换

同时,我还想给大伙介绍一下组件库:https://github.com/lzj960515/…

该组件库为我司外部的后端组件库——局部组件,外面的组件曾经久经考验,能够放心使用。

应用这些组件,能够将你的编码效率晋升 … 反正总是会有晋升的,我用着是特爽。

如果你想参加进来,欢送之至,如果你发现外面有 bug,感激大佬!

正文完
 0