SpringbootRedis分布式锁
SpringbootRedis分布式锁
1. 添加相关依赖
<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>
2. 创建分布式锁注解
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @author devcxl
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
/**
* 是否只针对请求
*
* @return
*/
boolean api();
/**
* 锁key前缀
*/
String keyPrefix() default "lock_key";
/**
* 锁value
*/
String valuePrefix() default "lock_value";
/**
* 锁超时时间
*/
long timeout() default 10;
/**
* 超时时间单位
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
3. aop实现
import cn.devcxl.common.annotation.DistributedLock;
import cn.devcxl.common.constant.SysConstant;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* @author devcxl
*/
@Slf4j
@Aspect
@Component
public class DistributedLockAspect {
/**
* StringRedisTemplate
*/
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 定义切点
*/
@Pointcut("@annotation(cn.devcxl.common.annotation.DistributedLock)")
public void apiPointCut() {
}
/**
* 切点
* 加分布式锁
*/
@Around(value = "apiPointCut() && @annotation(distributedLock)")
public Object apiAround(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Exception {
boolean api = distributedLock.api();
String key = null;
String value = null;
if (api) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
//访问者ip
String clientIP = ServletUtil.getClientIP(request);
//请求方式
String method = request.getMethod();
//请求路径
String uri = request.getRequestURI();
//获取入参
Object[] args = pjp.getArgs();
// 类名方法名作为分布式锁的key
key = distributedLock.keyPrefix() + "_" + clientIP + "_" + method + "_" + uri + "_" + JSONUtil.toJsonStr(args);
value = distributedLock.valuePrefix();
} else {
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
// 类名方法名作为分布式锁的key
key = distributedLock.keyPrefix() + "_" + className + "." + methodName;
value = distributedLock.valuePrefix() + "_" + SysConstant.HOSTNAME;
}
// 获取锁
Boolean status = stringRedisTemplate.opsForValue().setIfAbsent(key, value, distributedLock.timeout(), distributedLock.timeUnit());
if (!ObjectUtils.isEmpty(status) && status.equals(Boolean.TRUE)) {
try {
Object proceed = pjp.proceed();
// 处理完成后解锁
stringRedisTemplate.delete(key);
return proceed;
} catch (Throwable throwable) {
log.error("key failure!! key{} error:{}", key, throwable.getMessage());
}
}
log.warn("getLock failure!! key{}", key);
throw new Exception("请勿重复操作!!");
}
}
4. 使用示例
@PostMapping("/register")
@DistributedLock(api = true, keyPrefix = "register_", timeout = 3)
public Resp<String> register(@RequestBody @Validated RegisterParam param) {
registerServiceImpl.register(param);
return Resp.ok();
}
or
@Slf4j
@Component
public class BatchHandlerJob{
@Scheduled(cron = "0 0 18 * * ?")
@DistributedLock(api = false, keyPrefix = "handler_job_xxx", timeout = 70)
public void handler(){
// handler work
}
// ...
}