Redis分布式锁实现

Redis锁的优势

对于分布式服务的情况下,当只使用java原生相关锁(ReentrantLock或Synchronize)操作时,只能保证一个jvm进程中的操作受到锁的保护,但对于多个jvm进程就无法进行有效锁保护控制;

因此在分布式环境下,如果我们想要并发严格控制资源,那么就需要用到分布式锁;本文讲述了通过RedisLock来实现分布式锁.

1.创建一个分布式锁注解 DistributeLock

import java.lang.annotation.Documented;
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)
@Documented
public @interface DistributeLock {
    
    /**
     * 缓存 key 前缀,必填
     *
     * @return String
     */
    String prefix() default "";
    
    /**
     * 缓存 key, 支持 spel 表达式,与字符常量
     *
     * @return String
     */
    String[] cacheKey() default "";
    
    /**
     * 缓存秒数
     *
     * @return int
     */
    int lockTime() default 1;
}

2.分布式锁拦截器实现

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
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.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 分布式锁拦截器~
 *
 */
@Aspect
@Component
@Order(1)
@RequiredArgsConstructor
public class DistributeLockAspect {
    
    private final RedisLockRegistry redisLockRegistry;
    
    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    
    private static final ExpressionParser PARSER = new SpelExpressionParser();
    
    @Pointcut("@annotation(com.wmeimob.mall.service.aop.DistributeLock)")
    public void pointCut() {
    }
    
    @Around("pointCut()")
    public Object myAround(ProceedingJoinPoint point) throws Throwable {
        Class targetClass = point.getTarget().getClass();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = targetClass.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        DistributeLock distributeLock = AnnotationUtil.getAnnotation(method, DistributeLock.class);
        if (distributeLock == null) {
            return point.proceed();
        }
        Assert.notEmpty(distributeLock.cacheKey(), "请设置缓存key参数");
        String cacheKey = getSpelDefinitionKey(distributeLock.cacheKey(), point.getThis(), method, point.getArgs());
        String prefix = distributeLock.prefix();
        Lock lock = this.redisLockRegistry.obtain(prefix + cacheKey);
        try {
            if (lock.tryLock(distributeLock.lockTime(), TimeUnit.SECONDS)) {
                return point.proceed();
            }
            //这里需要自己做一个全局异常捕捉返回
            throw new GlobalCustomizationException("您的操作过于频繁,请稍后重试");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                lock.unlock();
            } catch (IllegalStateException e) {
            }
        }
    }
    
    private String getSpelDefinitionKey(String[] definitionKeys, Object rootObject, Method method,
                                        Object[] parameterValues) {
        StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, parameterValues,
                NAME_DISCOVERER);
        List definitionKeyList = new ArrayList<>(definitionKeys.length);
        for (String definitionKey : definitionKeys) {
            if (definitionKey.startsWith("#")) {
                Object result = PARSER.parseExpression(definitionKey).getValue(context);
                if (ObjectUtil.isNotNull(result)) {
                    definitionKeyList.add(result.toString());
                }
            } else {
                definitionKeyList.add(definitionKey);
            }
        }
        return StringUtils.collectionToDelimitedString(definitionKeyList, ".", "", "");
    }
}

3.具体使用地方

    /**
     * 保存用户
     */
    @PostMapping("saveUser")
	@DistributeLock(prefix = "saveUser:", cacheKey = "#req.mobile", lockTime = 2)
    public Object saveUser(SaveUserReq req) {
		//具体业务逻辑
        try {
            //睡眠10s方便测试
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "";
    }

你可能感兴趣的:(SpringBoot,Spring框架,redis,分布式,数据库)