微服务Redis分布式锁配置成注解使用(AOP)

在分布式系统中,因为每个进程或机器都是独立的,它们之间并不能共享同一个锁,因此需要通过一些方法来实现锁的协调。而 Redis 分布式锁就是一种基于 Redis 实现的分布式锁算法,它可以让多个进程或机器安全地共享同一个锁,从而协调它们的执行顺序和时间。它的主要作用是在多个进程或多台机器之间协调执行某个任务,以保证任务的正确性和一致性。

我之前有写过一篇通过Redisson+RLock在方法中手动实现分布式锁,而这有个缺陷就是需要自行处理锁的释放、异常处理等问题,而且可读性和维护性都比较低。所以下面将结合 Spring 的 Aop 来实现自定义注解 Redis 分布式锁。

一、引入依赖

首先,要确保项目中引入了 Redis、Redisson、AOP 相关依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.17.0</version>
</dependency>

二、创建自定义注解

我这里创建一个 @RedisLock 注解,用于标记需要加锁的方法

java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
    // 锁key值
    String value();

    // 锁的过期时间,默认为60秒
    long expireSeconds() default 60;
}

@Target(ElementType.METHOD): 这个注解表示该注解可以应用在方法上。ElementType.METHOD 表示目标元素是方法。
@Retention(RetentionPolicy.RUNTIME): 这个注解表示该注解在运行时保留。RetentionPolicy.RUNTIME 表示注解在运行时可见,可以通过反射获取注解信息。

三、创建切面(Aspect)类

创建一个RedisLockAspect类,该类用于实现对 @RedisLock 注解方法的拦截和处理逻辑

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RedisLockAspect {
	// 注入redisson客户端
    @Autowired
    private RedissonClient redissonClient;

	// 表示这个 Advice 将会应用到所有带有'RedisLock'注解的方法上。
    @Around("@annotation(redisLock)")
    public Object distributedLock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        // 锁的key值
        String lockKey = redisLock.value();
        // 超时时间
        long expireSeconds = redisLock.expireSeconds();
        RLock lock = redissonClient.getLock(lockKey);
        boolean isLocked = false;

        try {
            // 尝试获取分布式锁,若获取失败则等待或执行其他逻辑
            isLocked = lock.tryLock(expireSeconds, TimeUnit.SECONDS);
            if (isLocked) {
                // 成功获取锁,执行业务逻辑
                return joinPoint.proceed();
            } else {
                // 获取锁超时,可以根据实际情况处理,如抛出自定义异常等
                throw new RuntimeException("Failed to acquire lock for method: " + joinPoint.getSignature().toShortString());
            }
        } finally {
            if (isLocked) {
                // 释放锁
                lock.unlock();
            }
        }
    }
}

四、配置 Redisson 注入 bean

在 RedisConfig 中配置 Redisson 客户端,加入到spring容器

java 复制代码
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class RedisConfig {
    @Bean
    public RedissonClient redisson() {
        Config config = new Config();
        // 用自己的redis连接
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        config.useSingleServer().setPassword("xxxx");
        return Redisson.create(config);
    }
}

现在,基本配置就完成了

五、测试使用

在需要加锁的方法上使用 @RedisLock 注解,并指定锁的 Key 和超时时间,我这里写了一个demo,在方法中睡眠5秒钟,然后分别调用,看看是否成功锁定

java 复制代码
@RestController
@RequestMapping("/demo")
public class DemoController extends Thread{

    @GetMapping
    @RedisLock(value = "demoKey", expireSeconds = 10)
    public ApiResponse<Void> updatePassword(@RequestParam String str) throws InterruptedException {
        System.out.println("任务"+str+"开始...");
        sleep(5000);
        System.out.println("任务"+str+"完成...");
        return ApiResponse.success();
    }

}

用 postman 在五秒内调用两次,一次参数为1,一次为2

输出结果可以看到,任务2是等待任务1完成后才开始执行的,所以锁定成功啦

超时测试

设置超时时间为5秒,睡眠时间10秒...

java 复制代码
	@GetMapping
    @RedisLock(value = "demoKey", expireSeconds = 5)
    public ApiResponse<Void> updatePassword(@RequestParam String str) throws InterruptedException {
        System.out.println("任务"+str+"开始...");
        sleep(10000);
        System.out.println("任务"+str+"完成...");
        return ApiResponse.success();
    }

分别调用后,可以看到,超时报错了


以上就是一个将 Redis 分布式锁配置成注解使用的示例。这只是其中一种实现方式,你可以根据自己的实际需求进行调整和扩展。希望对你有所帮助!

相关推荐
FIN技术铺2 小时前
Redis集群模式之Redis Sentinel vs. Redis Cluster
数据库·redis·sentinel
斑驳竹影3 小时前
【RabbitMQ】之高可用集群搭建
分布式·rabbitmq
floret*4 小时前
Kafka高频面试题详解
分布式·kafka
Wlq04154 小时前
分布式技术缓存技术
分布式·缓存
明达技术5 小时前
LVDS高速背板总线:打造分布式I/O高效数据传输新境界
分布式·物联网·自动化·lvds
java1234_小锋5 小时前
分布式环境下宕机的处理方案有哪些?
分布式
大数据魔法师5 小时前
Hadoop生态圈框架部署(六)- HBase完全分布式部署
hadoop·分布式·hbase
知否&知否6 小时前
kafka中是如何快速定位到一个offset的
分布式·kafka
林戈的IT生涯6 小时前
一个基于Zookeeper+Dubbo3+SpringBoot3的完整微服务调用程序示例代码
微服务·rpc·dubbo
程序员曦曦6 小时前
一文熟悉redis安装和字符串基本操作
自动化测试·软件测试·数据库·redis·功能测试·程序人生·缓存