注解形式实现,Redis分布式锁

Redis工具类参考我的博文:https://blog.csdn.net/weixin_38399962/article/details/82753763

一个注解就可以实现分布式锁?这么神奇么?

首先定义注解:

/**
 * Description:分布式Redis锁
 * User: zhouzhou
 * Date: 2018-09-25
 * Time: 10:55
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface DLock {

    String lockName() default ""; // 锁名
    int retryTimes() default 0; // 重试次数
    long retryWait() default 0; // 重试等待时间,单位 : ms

}

再定义切面:

 *     先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试)
 *     如果没有重试次数,方法返回null 记得捕获NP
 *     当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔

/**
 * Description: 分布式锁
 * <p>
 *     先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试)
 *     如果没有重试次数,方法返回null 记得捕获NP
 *     当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔
 * </p>
 * User: zhouzhou
 * Date: 2018-09-25
 * Time: 10:58
 * version: 1.0
 */
@Aspect
@Component
public class DLockAspect {

    private static final Logger log = LoggerFactory.getLogger(DLockAspect.class);
    private static final String LOCK_NAME = "lockName";
    private static final String RETRY_TIMES = "retryTimes";
    private static final String RETRY_WAIT = "retryWait";


    @Autowired
    private CommonRedisHelper redisHelper;

    @Pointcut("@annotation(com.shuige.components.cache.annotation.DLock)")
    public void dLockAspect() {
    }

    @Around("dLockAspect()")
    public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable {
        System.out.println("分布式锁测试");
        //获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
        String lockName = (String) annotationArgs.get(LOCK_NAME);
        Assert.notNull(lockName, "分布式,锁名不能为空");
        int retryTimes = (int) annotationArgs.get(RETRY_TIMES);
        long retryWait = (long) annotationArgs.get(RETRY_WAIT);

        // 获取锁
        boolean lock = redisHelper.lock(lockName);
        if (lock) {
            // 执行主逻辑
            return execut(proceeding, lockName);
        } else {
            // 如果重试次数为零, 则不重试
            if (retryTimes <= 0) {
                log.info(String.format("{%s}已经被锁, 不重试", lockName));
                throw new RuntimeException(String.format("{%s}已经被锁, 不重试", lockName));
            }

            if (retryWait == 0) {
                retryWait = 200;
            }
            // 设置失败次数计数器, 当到达指定次数时, 返回失败
            int failCount = 1;
            while (failCount <= retryTimes) {
                // 等待指定时间ms
                try {
                    Thread.sleep(retryWait);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (redisHelper.lock(lockName)) {
                    // 执行主逻辑
                    return execut(proceeding, lockName);
                } else {
                    log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", lockName, failCount, retryTimes, retryWait));
                    failCount++;
                }
            }
            throw new RuntimeException("系统繁忙, 请稍等再试");
        }

    }

    private Object execut(ProceedingJoinPoint proceeding, String lockName) throws Throwable {
        try {
            // 执行主逻辑
            Object proceed = proceeding.proceed();
            return proceed;
        } catch (Throwable throwable) {
            log.error(String.format("执行分布式锁发生异常锁名:{%s},异常名称:{%s}", lockName, throwable.getMessage()));
            throw throwable;
        } finally {
            redisHelper.delete(lockName);
        }
    }

    /**
     * 获取锁参数
     *
     * @param proceeding
     * @return
     */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Map<String, Object> result = new HashMap<String, Object>();
                DLock redisLock = method.getAnnotation(DLock.class);
                String firstArg = getFirstArg(proceeding);
                result.put(LOCK_NAME, redisLock.lockName().concat("_").concat(firstArg));
                result.put(RETRY_TIMES, redisLock.retryTimes());
                result.put(RETRY_WAIT, redisLock.retryWait());
                return result;
            }
        }
        return null;
    }

    /**
     * 获取第一个String类型的参数为锁的业务参数
     *
     * @param proceeding
     * @return
     */
    public String getFirstArg(ProceedingJoinPoint proceeding) {
        Object[] args = proceeding.getArgs();
        if (args != null && args.length > 0) {
            for (Object object : args) {
                String type = object.getClass().getName();
                if ("java.lang.String".equals(type)) {
                    return (String) object;
                }
            }
        }
        return null;
    }

}

使用场景:

在方法上定义注解即可, redis的key就是 lockName_cityCode, 重试次数5次, 重试间隔300ms

猜你喜欢

转载自blog.csdn.net/weixin_38399962/article/details/82848395