目录

自定义重试注解

目录
1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
package com.gsm.bo.task.provider.common;

import java.lang.annotation.*;

/**
 * @author Ethan Wang
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {

  /**
   * Exception type that are retryable.
   *
   * @return exception type to retry
   */
  Class<? extends Throwable> value() default RuntimeException.class;

  /**
   * 包含第一次失败
   *
   * @return the maximum number of attempts (including the first failure), defaults to 3
   */
  int maxAttempts() default 3;

}


package com.gsm.bo.task.provider.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

/**
 * @author Ethan Wang
 */
@Aspect
@Component
public class RetryAspect {
  public static final Logger log = LoggerFactory.getLogger(RetryAspect.class);

  @Pointcut("@annotation(com.gsm.bo.task.provider.common.Retryable)")
  public void myPointcut() {
    // Pointcut
  }

  @Around("myPointcut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {
    Method method = getCurrentMethod(point);
    Retryable retryable = method.getAnnotation(Retryable.class);

    //1. 最大次数判断
    int maxAttempts = retryable.maxAttempts();
    if (maxAttempts <= 1) {
      return point.proceed();
    }

    //2. 异常处理
    int times = 0;
    long timeout = 0;
    final Class<? extends Throwable> exceptionClass = retryable.value();
    while (true) {
      try {
        return point.proceed();
      } catch (Throwable e) {
        if (exceptionClass.isInstance(e)) {
          times++;
          timeout = timeout + times * 3L;
          // 3 9 18 38 45 63 84
          log.warn("retry cause by {} after {} {}", exceptionClass.getName(), timeout, TimeUnit.MINUTES);
          TimeUnit.MINUTES.sleep(timeout);
        } else {
          throw e;
        }
        // 超过最大重试次数 or 不属于当前处理异常
        if (times >= maxAttempts) {
          throw new RuntimeException("##########  重试多次连接 Goldoffice_api  ##########\"", e);
        }
      }
    }
  }

  private Method getCurrentMethod(ProceedingJoinPoint point) {
    try {
      Signature sig = point.getSignature();
      MethodSignature ms = (MethodSignature) sig;
      Object target = point.getTarget();
      return target.getClass().getMethod(ms.getName(), ms.getParameterTypes());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }
}

Spring Retry 有更完善的功能,可以直接使用。