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

前言

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

如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

实现分布式锁的方式多种多样,但一般来说都是使用编码的方式在业务代码中穿插加锁解锁逻辑。

这种方式优点是十分模版化,几乎不会出错,每一套加锁解锁逻辑都是一模一样。

缺点也是这个,虽说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/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D

AOP切面设计如下:

lock_design

编码

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/kq-universal/tree/main/kq-universal-redis-starter

如果你想知道其中的实现,欢迎clone代码阅读或者与我交流

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

该组件库为我司内部的后端组件库——部分组件,里面的组件已经久经考验,可以放心使用。

使用这些组件,可以将你的编码效率提升…反正总是会有提升的,我用着是特爽。

如果你想参与进来,欢迎之至,如果你发现里面有bug,感谢大佬!

自定义注解实现分布式锁是一种在分布式系统中解决并发问题的方法。通过在需要加锁的方法上添加自定义注解,可以实现对该方法的并发控制。具体实现步骤如下: 1. 首先,在项目的pom文件中引入相关的依赖,以支持分布式锁的功能。 2. 在项目中创建一个新的目录,并使用注解来定义自定义注解。这个自定义注解可以用来标记需要加锁的方法。 3. 在具体的业务实现类中,使用自定义注解来标记需要加锁的方法。这样,在方法执行时,会根据注解的配置来进行并发控制。 使用自定义注解实现分布式锁的好处是可以避免在每个需要加锁的方法中都引入相关的配置类和方法,提高了代码的可读性和可维护性。此外,还可以将配置和项目打包成jar包,方便在其他项目中引入使用分布式锁相对于普通锁的区别在于,普通锁只能在单体应用中锁住方法,而分布式锁可以在分布式系统中实现并发控制。在分布式系统中,不同实例之间共用代码和数据库,因此需要使用分布式锁来保证并发操作的正确性。分布式锁可以使用一些工具,如Redis,将锁放在共享的资源中,以实现多个实例共用一个锁的效果。 总结来说,自定义注解实现分布式锁是一种方便且可扩展的方式,可以在分布式系统中解决并发问题。 #### 引用[.reference_title] - *1* [自定义注解实现分布式锁](https://blog.csdn.net/qq_37205211/article/details/122140430)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [分布式编程-实现分布式锁-优雅的使用自定义注解实现](https://blog.csdn.net/qq_41692766/article/details/105842467)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [自定义注解,基于redis实现分布式锁](https://blog.csdn.net/weixin_43975276/article/details/131097829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值