title: “分布式锁:Redis+注解”
url: “https://wsk1103.github.io/”
tags:
- 学习笔记
- 分布式锁
1. 起因
使用 redis 来设计分布式锁,经常需要在每个方法开始执行 setNx(key) ,然后在方法运行结束后 del(key) 。而且当系统需要改进使用分布式锁的时候,需要修改的地方偏多,这样造成了过程繁杂,而且有时候又忘记 del(key) ,导致间隔比较短请求被拦截。所以考虑设计一个结合 注解 和 AOP 来完成分布式锁。
2. 思路流程图
3. 代码设计
3.1 增加依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<scope>runtime</scope>
</dependency>
3.2 redis.setNx() 和 redis.del()
/**
* 分布式锁
* 设置成功,返回1,
*
* @param key
* @param value
* @param time 过期时间,单位秒
* @return
*/
public static boolean setNx(String key, String value, int time) {
Jedis jedis = null;
try {
if (StringUtils.isBlank(key)) {
throw new NullPointerException("The key is not allowed null");
}
jedis = RedisDB.getPool().getResource();
long result = jedis.setnx(key, value);
if (result == 1) {
// ==1 ,设置成功
jedis.expire(key, time);
return true;
} else {
return false;
}
} catch (Exception e) {
ILogUtil.error("setNx fail:{}", e);
return false;
} finally {
RedisDB.getPool().returnResourceObject(jedis);
}
}
/**
* 删除redis里的主键
*
* @param key redis主键
*/
public static void del(String key) {
Jedis jedis = RedisDB.getPool().getResource();
try {
jedis.del(key);
} finally {
// 使用完后,将连接放回连接池
RedisDB.getPool().returnResourceObject(jedis);
}
}
3.3 注解 @MyLock
import java.lang.annotation.*;
/**
* @author sk
* @time 2020/1/15
* @desc say
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLock {
}
3.4 设计 AOP
package com.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @author sk
* @time 2020/1/15
* @desc say
**/
@Aspect
@Component
public class LockAspect {
@Before("@annotation(com.anno.MyLock)")
public void before(JoinPoint joinPoint) throws Exception {
System.out.println("before");
Object[] objects = joinPoint.getArgs();
for (Object o : objects) {
if (o instanceof String) {
String method = joinPoint.getSignature().getName();
String key = method + o.toString();
boolean b = setNx(key, "", 60);
if (!b) {
throw new Exception("请勿重复操作");
} else {
System.out.println("=======获取lock成功!!!!!!========");
}
break;
}
}
}
@After("@annotation(com.anno.MyLock)")
public void after(JoinPoint joinPoint) throws Exception {
System.out.println("after");
Object[] objects = joinPoint.getArgs();
for (Object o : objects) {
if (o instanceof String) {
String method = joinPoint.getSignature().getName();
String key = method + o.toString();
del(method + o.toString());
System.out.println("释放锁成功");
break;
}
}
}
}
3.5 方法注入
import com.masget.business.anno.MyLock;
import org.springframework.stereotype.Service;
/**
* @author sk
* @time 2020/1/15
* @desc say
**/
@Service
public class MyService {
@MyLock
public String go(String go) {
System.out.println(go);
return "dd";
}
}
4. 注意和设计优化
- 4.1. 在AOP拦截时,是通过该方法入参中的第一个 String 来作为 lock 的 key ,所以在调用方法时,需要使该 String 能够识别用户,所以该 key 一般为用户的身份标识,例如 sessionId 。
- 4.2. 或者将入参统一修改为一个 抽象类 ,该抽象类里面有一个 用户唯一标识 的字段,然后其他类继承该类。
例如
import lombok.Data;
/**
* @author wsk1103
* @date 2020/1/15
* @description 描述
*/
@Data
public abstract class AbstractRequest {
private String sessionId;
}
对应的 AOP 修改为:
@Before("@annotation(com.anno.MyLock)")
public void before(JoinPoint joinPoint) throws Exception {
System.out.println("before");
Object[] objects = joinPoint.getArgs();
for (Object o : objects) {
if (o instanceof AbstractRequest) {
String method = joinPoint.getSignature().getName();
String key = method + ((AbstractRequest)o).getSessionId();
boolean b = setNx(key, "", 60);
if (!b) {
throw new Exception("请勿重复操作");
} else {
System.out.println("=======获取lock成功!!!!!!========");
}
break;
}
}
}
@After("@annotation(com.anno.MyLock)")
public void after(JoinPoint joinPoint) throws Exception {
System.out.println("after");
Object[] objects = joinPoint.getArgs();
for (Object o : objects) {
if (o instanceof AbstractRequest) {
String method = joinPoint.getSignature().getName();
String key = method + ((AbstractRequest)o).getSessionId();
del(method + o.toString());
System.out.println("释放锁成功");
break;
}
}
}
来源:CSDN
作者:wsk1103
链接:https://blog.csdn.net/wsk1103/article/details/103996552