微服务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 分布式锁配置成注解使用的示例。这只是其中一种实现方式,你可以根据自己的实际需求进行调整和扩展。希望对你有所帮助!

相关推荐
清风19811 小时前
kafka消息可靠性传输语义
数据库·分布式·kafka
小诸葛的博客1 小时前
Kafka、RocketMQ、Pulsar对比
分布式·kafka·rocketmq
数据智能老司机4 小时前
CockroachDB权威指南——SQL调优
数据库·分布式·架构
数据智能老司机4 小时前
CockroachDB权威指南——应用设计与实现
数据库·分布式·架构
数据智能老司机4 小时前
CockroachDB权威指南——CockroachDB 模式设计
数据库·分布式·架构
mghio15 小时前
Dubbo 中的集群容错
java·微服务·dubbo
数据智能老司机1 天前
CockroachDB权威指南——CockroachDB SQL
数据库·分布式·架构
数据智能老司机1 天前
CockroachDB权威指南——开始使用
数据库·分布式·架构
Kagol1 天前
macOS 和 Windows 操作系统下如何安装和启动 MySQL / Redis 数据库
redis·后端·mysql
数据智能老司机1 天前
CockroachDB权威指南——CockroachDB 架构
数据库·分布式·架构