redis分布式锁

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 hmget(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    public List hmget(String key, String... fields){
        return redisTemplate.opsForHash().multiGet(key,Arrays.asList(fields));
    }

    /**
     * 指定key的值自增,不指定步长,默认自增1
     * @param key
     * @return
     */
    public Long incr(String key) {
        return this.incr(key, 1);
    }

    public Long incr(String key,long incrStep) {
        return redisTemplate.opsForValue().increment(key, incrStep);
    }

    public void setObject(String key,Object value){
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getObject(String key){
        return (Object) redisTemplate.opsForValue().get(key);
    }

    public void zAdd(String key,String member,double score){
        redisTemplate.opsForZSet().add(key,member,score);
    }

    public boolean zExist(String key,String member){
        Long rank = redisTemplate.opsForZSet().rank(key, member);
        return rank != null;
    }

    public Double zScore(String key,String member){
        return redisTemplate.opsForZSet().score(key,member);
    }

    public Set zRangeByScore(String key, double min, double max){
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.rangeByScore(key, min, max);
    }

    public void zRemove(String key,String member){
        redisTemplate.opsForZSet().remove(key,member);
    }

    public void zRemove(String key,Object[] members){
        redisTemplate.opsForZSet().remove(key,members);
    }

}

你可能感兴趣的:(数据库)