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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 如何使用注解实现分布式锁

前言

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

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

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

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

缺点也是这个,虽说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,感谢大佬!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4月前
|
NoSQL 安全 Java
nicelock--一个注解即可使用Redis分布式锁!
Nicelock的引入为分布式系统中的资源同步访问提供了一个简单高效和可靠的解决方案。通过注解的方式,简化了锁的实现和使用,使开发人员可以将更多精力专注于业务逻辑的实现,而不是锁的管理。此外,Nicelock在保持简单易用的同时,也提供了足够的灵活性和可靠性,满足了不同应用场景下对分布式锁的需求。
70 1
|
7月前
|
缓存 NoSQL Java
【亮剑】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护,如何使用注解来实现 Redis 分布式锁的功能?
【4月更文挑战第30天】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护。基于 Redis 的分布式锁利用 SETNX 或 SET 命令实现,并考虑自动过期、可重入及原子性以确保可靠性。在 Java Spring Boot 中,可通过 `@EnableCaching`、`@Cacheable` 和 `@CacheEvict` 注解轻松实现 Redis 分布式锁功能。
140 0
|
存储 NoSQL Java
一个注解实现分布式锁
一个注解实现分布式锁
120 0
|
消息中间件 NoSQL JavaScript
Spring Boot加一个注解,轻松实现 Redis 分布式锁
Spring Boot加一个注解,轻松实现 Redis 分布式锁
35440 2
|
消息中间件 NoSQL JavaScript
使用注解实现redis分布式锁
使用注解实现redis分布式锁
|
NoSQL Java Redis
如何使用注解来实现 Redis 分布式锁的功能?
如何使用注解来实现 Redis 分布式锁的功能?
163 0
|
NoSQL Java Redis
完美,SpringBoot中使用注解来实现 Redis 分布式锁
对于耗时业务和核心数据,不能让重复的请求同时操作数据,避免数据的不正确,所以要使用分布式锁来对它们进行保护。有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数据库的数据进行操作,需要避免对之前的业务造成影响。
254 0
|
消息中间件 存储 关系型数据库
一个注解搞定分布式事务
一个注解搞定分布式事务
|
XML NoSQL Java
【SimpleFunction系列二.2】SpringBoot注解整合Redisson分布式锁
Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。
397 0
【SimpleFunction系列二.2】SpringBoot注解整合Redisson分布式锁
|
NoSQL Redis
注解的方式实现redis分布式锁
创建redisLock注解: import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.
1208 0