redis分布式锁,可用于多实例部署,但是任务只需要执行一个
注解:AsDistributeTask
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @auther
* @date 2019/5/18 14:41
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AsDistributeTask {
@AliasFor("taskName")
String value() default "";
@AliasFor("value")
String taskName() default "";
long timeoutMillis() default 60000L;
}
具体实现
import cn.manulife.dgt.core.redis.util.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @auther
* @date 2019/08/28
*/
@Aspect
@Component
@Slf4j
public class DistributeTaskAspect {
@Autowired
private RedisLock redisLock;
@Pointcut("@annotation(cn.manulife.dgt.core.redis.task.AsDistributeTask)")
private void distributeTaskPointcut(){}
@Around(value="distributeTaskPointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
AsDistributeTask asDistributeTask = AnnotationUtils.findAnnotation(methodSignature.getMethod(), AsDistributeTask.class);
String taskName = asDistributeTask.taskName();
if(StringUtils.isEmpty(taskName)){
taskName = methodSignature.getName();
}
log.info(taskName + ",任务开始执行");
Object result = null;
String lockValue = String.valueOf(System.currentTimeMillis() + asDistributeTask.timeoutMillis());
if(redisLock.lock(taskName, lockValue)) {
log.info(taskName + "竞争锁成功");
try {
result = proceedingJoinPoint.proceed();
} catch (Exception e) {
log.error(e.getMessage(), e);
}finally {
redisLock.unlock(taskName, lockValue);
log.info(taskName + ",释放锁成功");
}
}else{
log.info(taskName + "竞争锁失败");
}
log.info(taskName + ",任务执行结束");
return result;
}
}
redis锁的工具类
package cn.manulife.dgt.core.redis.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author
* @date 2019-05-21
*/
@Component
@Slf4j
public class RedisLock {
private final long LOCK_EXPIRE = 3000L;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 加锁
* @param key 唯一标志
* @param value 当前时间+超时时间 也就是时间戳
* @return
*/
public boolean lock(String key,String value){
// 对应setnx命令 可以成功设置,也就是key不存在
if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){
return true;
}
// 判断锁超时 - 防止原来的操作异常,没有运行解锁操作 防止死锁
String currentValue = stringRedisTemplate.opsForValue().get(key);
// 如果锁过期 currentValue不为空且小于当前时间
if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){
// 获取上一个锁的时间value 对应getset,如果key存在
String oldValue =stringRedisTemplate.opsForValue().getAndSet(key,value);
// 假设两个线程同时进来这里,因为key被占用了,而且锁过期了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。
// 而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的value已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。
if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){
// oldValue不为空且oldValue等于currentValue,也就是校验是不是上个对应的商品时间戳,也是防止并发
return true;
}
}
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key,String value){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value) ){
// 删除key
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("[Redis分布式锁] 解锁出现异常了,{}",e);
}
}
}
redis的工具类
package cn.manulife.dgt.core.redis.util;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @
* @date 2019-05-21
*/
@Slf4j
@Component
public class RedisUtil {
private Logger logger = LoggerFactory.getLogger(RedisUtil.class);
@Autowired
private RedisTemplate redisTemplate;
RedisUtil(){
logger.info("RedisUtil创建成功");
}
public void set(String key,String value){
redisTemplate.opsForValue().set(key, value);
}
public String get(String key){
return (String) redisTemplate.opsForValue().get(key);
}
public void hmset(String key, Map hash){
redisTemplate.opsForHash().putAll(key,hash);
}
public void hset(String key, String hk, String hv) {
redisTemplate.opsForHash().put(key, hk, hv);
}
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
public void hdel(String key,String hk){
redisTemplate.opsForHash().delete(key,hk);
}
public void hmdel(String key,String... hk){
redisTemplate.opsForHash().delete(key,hk);
}
public Map