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

相关推荐
luoganttcc2 分钟前
[源码解析] 模型并行分布式训练Megatron (2) --- 整体架构
分布式·架构·大模型
Hello.Reader1 小时前
Redis大Key问题全解析
数据库·redis·bootstrap
B1nna4 小时前
Redis学习(三)缓存
redis·学习·缓存
张铁铁是个小胖子9 小时前
消息中间件RabbitMQ和kafka
分布式·kafka·rabbitmq
time_silence9 小时前
微服务——数据管理与一致性
微服务·云原生·架构
神秘打工猴9 小时前
Spark任务的执⾏流程
大数据·分布式·spark
A227410 小时前
Redis——缓存雪崩
java·redis·缓存
白露与泡影10 小时前
Redisson分布式锁的源码解读
分布式·wpf
weisian15110 小时前
Redis篇--应用篇3--数据统计(排行榜,计数器)
数据库·redis·缓存
言之。10 小时前
Redis单线程快的原因
数据库·redis·缓存