Docker 系统性入门+进阶实践(2021最新版)

467 阅读3分钟

Docker 系统性入门+进阶实践(2021最新版)

超级架构师可能是最好的Redis散布式锁完成

1、引入redisson依赖

    org.redisson
    redisson
    3.16.2
Copy to clipboardErrorCopied
复制代码

2、自定义注解

/**
 * 散布式锁自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {
    /**
     * 锁的形式:假如不设置自动形式,当参数只要一个.运用 REENTRANT 参数多个 MULTIPLE
     */
    LockModel lockModel() default LockModel.AUTO;
    /**
     * 假如keys有多个,假如不设置,则运用 联锁
     *
     * @return
     */
    String[] keys() default {};
    /**
     * key的静态常量:当key的spel的值是LIST、数组时运用+号衔接将会被spel以为这个变量是个字符串,只能产生一把锁,达不到我们的目的,
     * 而我们假如又需求一个常量的话。这个参数将会在拼接在每个元素的后面
     *
     * @return
     */
    String keyConstant() default "";
    /**
     * 锁超时时间,默许30000毫秒(可在配置文件全局设置)
     *
     * @return
     */
    long watchDogTimeout() default 30000;
    /**
     * 等候加锁超时时间,默许10000毫秒 -1 则表示不断等候(可在配置文件全局设置)
     *
     * @return
     */
    long attemptTimeout() default 10000;
}
复制代码

3、常量类

/**
 * Redisson常量类
 */
public class RedissonConst {
    /**
     * redisson锁默许前缀
     */
    public static final String REDISSON_LOCK = "redisson:lock:";
    /**
     * spel表达式占位符
     */
    public static final String PLACE_HOLDER = "#";
}
复制代码

4、枚举

/**
 * 锁的形式
 */
public enum LockModel {
    /**
     * 可重入锁
     */
    REENTRANT,
    /**
     * 公平锁
     */
    FAIR,
    /**
     * 联锁
     */
    MULTIPLE,
    /**
     * 红锁
     */
    RED_LOCK,
    /**
     * 读锁
     */
    READ,
    /**
     * 写锁
     */
    WRITE,
    /**
     * 自动形式,当参数只要一个运用 REENTRANT 参数多个 RED_LOCK
     */
    AUTO
}
复制代码

5、自定义异常

/**
 * 散布式锁异常
 */
public class ReddissonException extends RuntimeException {
    public ReddissonException() {
    }
    public ReddissonException(String message) {
        super(message);
    }
    public ReddissonException(String message, Throwable cause) {
        super(message, cause);
    }
    public ReddissonException(Throwable cause) {
        super(cause);
    }
    public ReddissonException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
复制代码

6、AOP切面

   /**
 * 散布式锁aop
 */
@Slf4j
@Aspect
public class LockAop {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedissonProperties redissonProperties;
    @Autowired
    private LockStrategyFactory lockStrategyFactory;
    @Around("@annotation(lock)")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Lock lock) throws Throwable {
        // 需求加锁的key数组
        String[] keys = lock.keys();
        if (ArrayUtil.isEmpty(keys)) {
            throw new ReddissonException("redisson lock keys不能为空");
        }
        // 获取办法的参数名
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod());
        Object[] args = proceedingJoinPoint.getArgs();
        // 等候锁的超时时间
        long attemptTimeout = lock.attemptTimeout();
        if (attemptTimeout == 0) {
            attemptTimeout = redissonProperties.getAttemptTimeout();
        }
        // 锁超时时间
        long lockWatchdogTimeout = lock.watchdogTimeout();
        if (lockWatchdogTimeout == 0) {
            lockWatchdogTimeout = redissonProperties.getLockWatchdogTimeout();
        }
        // 加锁形式
        LockModel lockModel = getLockModel(lock, keys);
        if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.RED_LOCK) && keys.length > 1) {
            throw new ReddissonException("参数有多个,锁形式为->" + lockModel.name() + ",无法匹配加锁");
        }
        log.info("锁形式->{},等候锁定时间->{}毫秒,锁定最长时间->{}毫秒", lockModel.name(), attemptTimeout, lockWatchdogTimeout);
        boolean res = false;
        // 战略形式获取redisson锁对象
        RLock rLock = lockStrategyFactory.createLock(lockModel, keys, parameterNames, args, lock.keyConstant(), redissonClient);
        //执行aop
        if (rLock != null) {
            try {
                if (attemptTimeout == -1) {
                    res = true;
                    //不断等候加锁
                    rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS);
                } else {
                    res = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);
                }
                if (res) {
                    return proceedingJoinPoint.proceed();
                } else {
                    throw new ReddissonException("获取锁失败");
                }
            } finally {
                if (res) {
                    rLock.unlock();
                }
            }
        }
        throw new ReddissonException("获取锁失败");
    }
    /**
     * 获取加锁形式
     *
     * @param lock
     * @param keys
     * @return
     */
    private LockModel getLockModel(Lock lock, String[] keys) {
        LockModel lockModel = lock.lockModel();
        // 自动形式:优先匹配全局配置,再判别用红锁还是可重入锁
        if (lockModel.equals(LockModel.AUTO)) {
            LockModel globalLockModel = redissonProperties.getLockModel();
            if (globalLockModel != null) {
                lockModel = globalLockModel;
            } else if (keys.length > 1) {
                lockModel = LockModel.RED_LOCK;
            } else {
                lockModel = LockModel.REENTRANT;
            }
        }
        return lockModel;
    }
}
复制代码

这里运用了战略形式来对不同的锁类型提供完成。

7、锁战略的完成

先定义锁战略的笼统基类(也能够用接口):

/**
 * 锁战略笼统基类
 */
@Slf4j
abstract class LockStrategy {
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 创立RLock
     *
     * @param keys
     * @param parameterNames
     * @param args
     * @param keyConstant
     * @return
     */
    abstract RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient);
    /**
     * 获取RLock
     *
     * @param keys
     * @param parameterNames
     * @param args
     * @param keyConstant
     * @return
     */
    public RLock[] getRLocks(String[] keys, String[] parameterNames, Object[] args, String keyConstant) {
        List rLocks = new ArrayList<>();
        for (String key : keys) {
            List valueBySpel = getValueBySpel(key, parameterNames, args, keyConstant);
            for (String s : valueBySpel) {
                rLocks.add(redissonClient.getLock(s));
            }
        }
        RLock[] locks = new RLock[rLocks.size()];
        int index = 0;
        for (RLock r : rLocks) {
            locks[index++] = r;
        }
        return locks;
    }
    /**
     * 经过spring Spel 获取参数
     *
     * @param key            定义的key值 以#开头 例如:#user
     * @param parameterNames 形参
     * @param args           形参值
     * @param keyConstant    key的常亮
     * @return
     */
    List getValueBySpel(String key, String[] parameterNames, Object[] args, String keyConstant) {
        List keys = new ArrayList<>();
        if (!key.contains(PLACE_HOLDER)) {
            String s = REDISSON_LOCK + key + keyConstant;
            log.info("没有运用spel表达式value->{}", s);
            keys.add(s);
            return keys;
        }
        // spel解析器
        ExpressionParser parser = new SpelExpressionParser();
        // spel上下文
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        Expression expression = parser.parseExpression(key);
        Object value = expression.getValue(context);
        if (value != null) {
            if (value instanceof List) {
                List valueList = (List) value;
                for (Object o : valueList) {
                    keys.add(REDISSON_LOCK + o.toString() + keyConstant);
                }
            } else if (value.getClass().isArray()) {
                Object[] objects = (Object[]) value;
                for (Object o : objects) {
                    keys.add(REDISSON_LOCK + o.toString() + keyConstant);
                }
            } else {
                keys.add(REDISSON_LOCK + value.toString() + keyConstant);
            }
        }
        log.info("spel表达式key={},value={}", key, keys);
        return keys;
    }
}

download