zoukankan      html  css  js  c++  java
  • 分布式-技术专区-Redis分布式锁实现-第二步

    再上次篇章中汇集了相关的分布式锁的概念进行控制,接下来我们采用的是注解声明式开发服务方案,进行声明式开发代替编程式开发方案。 
    1.利用aop实现分布式锁
    2.只用在方法上加个注解,同时加上了重试机制

    1.前提我们可以个性化定制一些注解例如:RedisLock注解操作

    @RedisLock(lockPrefix = AbstractRedisContants.DIST_LOCK_FUND, lockParameter = "fundId")
    public void handle(FundAmountOptTypeEnum optType, Long fundId, BigDecimal amount, String operator, String remark) {
      strategyMap.get(optType).handle(fundId, amount, operator, remark);
    }
     

    2.定义RedisLock注解开发服务

    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;

    import org.springframework.core.annotation.Order;

    /**
    * redis锁注解
    * @date 2018/12/27
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    @Documented
    @Order(value = 10)
    public @interface RedisLock {
      // 锁前缀
      String lockPrefix() default "";

      // 方法参数名(用于取参数名的值与锁前缀拼接成锁名),尽量不要用对象map等,对象会toString后与锁前缀拼接
      String lockParameter() default "";

      // 尝试加锁,最多等待时间(毫秒)
      long lockWait() default 3000L;

      // 自动解锁时间 (毫秒)
      long autoUnlockTime() default 10000L;

      // 重试次数
      int retryNum() default 0;

      // 重试等待时间 (毫秒)
      long retryWait() default 500L;

    }

      @Order(value = 10)
      当使用了@Transactional 或其它切面时,相当于在执行执行多次次AOP切面。那么我们需要通过order 属性去定义AOP切面的先后执行顺序。 order越小,在AOP的chain 中越靠前,越先执行。(chain模式)

     3.RedisLockAspect

    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;

    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.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.util.Assert;

    import com.feitai.jieya.server.common.annotation.RedisLock;
    import com.feitai.jieya.server.common.exception.BusinessException;
    import com.feitai.jieya.server.utils.StringUtils;

    import lombok.extern.slf4j.Slf4j;

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

    private static final String LOCK_NAME = "lockName";
    private static final String lOCK_WAIT = "lockWait";
    private static final String AUTO_UNLOCK_TIME = "autoUnlockTime";
    private static final String RETRY_NUM = "retryNum";
    private static final String RETRY_WAIT = "retryWait";

    /**
    * redis工具类
    */
    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.feitai.jieya.server.common.annotation.RedisLock)")
    public void lockAspect() {}

    @Around("lockAspect()")
    public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable {

      // 获取注解中的参数
      Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
      String lockName = (String)annotationArgs.get(LOCK_NAME);
      Assert.notNull(lockName, "分布式,锁名不能为空");
      int retryNum = (int)annotationArgs.get(RETRY_NUM);
      long retryWait = (long)annotationArgs.get(RETRY_WAIT);
      long lockWait = (long)annotationArgs.get(lOCK_WAIT);
      long autoUnlockTime = (long)annotationArgs.get(AUTO_UNLOCK_TIME);

      // 获取锁
      RLock lock = redissonClient.getLock(lockName);
      try {
        boolean res = lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS);
      if (res) {
        // 执行主逻辑
        return proceeding.proceed();

      } else {
        // 如果重试次数为零, 则不重试
      if (retryNum <= 0) {
        log.info(String.format("{%s}已经被锁, 不重试", lockName));
        throw new BusinessException(String.format("{%s}已经被锁, 不重试", lockName));
      }

      if (retryWait == 0) {
        retryWait = 200L;
      }
      // 设置失败次数计数器, 当到达指定次数时, 返回失败
      int failCount = 1;
        while (failCount <= retryNum) {
        // 等待指定时间ms
        Thread.sleep(retryWait);
        if (lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS)) {
        // 执行主逻辑
        return proceeding.proceed();
      } else {
        log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", lockName, failCount, retryNum,retryWait));
        failCount++;
      }
      }
      throw new BusinessException("系统繁忙, 请稍等再试");
      }
      } catch (Throwable throwable) {
        log.error(String.format("执行分布式锁发生异常锁名:{%s},异常名称:{%s}", lockName, throwable.getMessage()));
        throw throwable;
      } finally {
        lock.unlock();
      }
    }

    /**
    * 获取锁参数
    *
    * @param proceeding
    * @return
    */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
    // if (!(objs[i] instanceof ExtendedServletRequestDataBinder)
    // && !(objs[i] instanceof HttpServletResponseWrapper)) {

      proceeding.getArgs();
      Object[] objs = proceeding.getArgs();
      String[] argNames = ((MethodSignature)proceeding.getSignature()).getParameterNames(); // 参数名

      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>();
          RedisLock redisLock = method.getAnnotation(RedisLock.class);

          if (StringUtils.isNotBlank(redisLock.lockParameter())) {
            for (int i = 0; i < objs.length; i++) {
              if (redisLock.lockParameter().equals(argNames[i])) {
                result.put(LOCK_NAME, redisLock.lockPrefix() + objs[i]);
                break;
              }

            }
          } else {
            result.put(LOCK_NAME, redisLock.lockPrefix());
          }
            result.put(lOCK_WAIT, redisLock.lockWait());
            result.put(AUTO_UNLOCK_TIME, redisLock.autoUnlockTime());
            result.put(RETRY_NUM, redisLock.retryNum());
            result.put(RETRY_WAIT, redisLock.retryWait());

            return result;
          }
         }
         throw new RuntimeException("异常");

      }

  • 相关阅读:
    UVA 1590 IP Networks
    UVA 12108
    HDUOJ 1042 N!
    UVA201 Squares
    UVaOJ 1339
    UVaOJ 202
    UVaOJ 100
    UVaOJ 10340
    第五章 跳跃表
    第四章 字典
  • 原文地址:https://www.cnblogs.com/liboware/p/11945005.html
Copyright © 2011-2022 走看看