Summary of Retry Mechanism Implementation

Native method

@Override
public String helloRerty(String msg) throws InterruptedException {
    
    
    int times = 0;
    while (times < 3) {
    
    
        try {
    
    
            if (msg.equals("error")) {
    
    
                throw new RuntimeException("error");
            }
        } catch (Exception e) {
    
    
            times++;
            log.info("times:{},time:{}", times, LocalDateTime.now());
            if (times == 3) {
    
    
                throw new RuntimeException("超过重试次数");
            }
            Thread.sleep(5000);
        }
    }

    return msg;
}

Dynamic proxy

/**
 * @Author shangkaihui
 * @Date 2020/5/23 19:28
 * @Desc 动态代理实现重试
 */
@Slf4j
public class RetryInvocationHandler implements InvocationHandler {
    
    

    private Object realTarget;

    public RetryInvocationHandler(Object realTarget) {
    
    
        this.realTarget = realTarget;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        int times = 0;
        while (times < 3) {
    
    
            try {
    
    
                return method.invoke(realTarget, args);
            } catch (Exception e) {
    
    
                times++;
                log.info("times:{},time:{}", times, LocalDateTime.now());
                if (times >= 3) {
    
    
                    throw new RuntimeException("超过超时次数");
                }
                Thread.sleep(1000);
            }
        }

        return null;
    }

    /**
     * 获取动态代理
     * @return
     */
    public static Object getProxy(Object realSubject) {
    
    
        InvocationHandler handler = new RetryInvocationHandler(realSubject);
        return Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
    }
}

CGLIB agent

/**
 * @Author shangkaihui
 * @Date 2020/5/23 22:26
 * @Desc
 */
@Slf4j
public class RetryCglibInterceptor implements MethodInterceptor {
    
    
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        int times = 0;
        while (times < 3) {
    
    
            try {
    
    
                return methodProxy.invokeSuper(o, objects);
            } catch (Exception e) {
    
    
                times++;
                log.info("times:{},time:{}", times, LocalDateTime.now());
                if (times >= 3) {
    
    
                    throw new RuntimeException("超过重试次数");
                }
                Thread.sleep(1000);
            }
        }
        return null;
    }

    public  Object getProxy(Class clazz) {
    
    
        Enhancer enhancer = new Enhancer();
        //目标对象类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //通过字节码技术创建目标对象类的子类实例作为代理
        return enhancer.create();
    }
}

Annotation + AOP implementation

@Target({
    
    ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retry {
    
    

    /**
     * 重试次数
     * @return
     */
    int maxTimes() default  3;

    /**
     * 休眠时间
     * @return
     */
    int sleepTime() default 1000;
}
@Component
@Aspect
@Slf4j
public class RetryAspect {
    
    

    @Pointcut(value = "@annotation(com.example.demo.retry.Retry)")
    public void myPointcut() {
    
    
    }

    @Around("myPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
    
    
        //获取注解
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        Retry retry = method.getAnnotation(Retry.class);
        int maxTime = retry.maxTimes();
        int sleepTime = retry.sleepTime();
        int times = 0;
        while (times < maxTime) {
    
    
            try {
    
    
                return point.proceed();

            } catch (Exception e) {
    
    
                times++;
                log.info("times:{},time:{}", times, LocalDateTime.now());
                if (times >= maxTime) {
    
    
                    throw new RuntimeException("超过超时次数");
                }
                Thread.sleep(sleepTime*times);
            }
        }
        return null;
    }

}

Spring Retry

pom introduction

<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
</dependency>

Application startup class opens retry

@EnableRetry
public class Application {
    .......
}

Mark @Retryable on the specified method to enable retry

@Retryable(value={A异常.class,B异常.class},
     maxAttempts=重试次数,
     backoff = @Backoff(delay = 延迟毫秒数,multiplier = 延迟倍数))
public void retryTest() throws Exception {
    System.out.println(Thread.currentThread().getName()+" do something...");
    throw new RemoteAccessException("RemoteAccessException....");
}

Mark @Recover on the specified method to enable the method to be called after the retry fails (note that it needs to be in the same class as the reprocessing method)

@Recover
  public void recover(A异常 e) {
    // ... do something
  }
  
  @Recover
  public void recover(B异常 e) {
    // ... do something
  }

Detailed explanation of annotation parameters

@Retryable annotation: the annotated method will retry when an exception occurs

value: specify the occurrence of the exception for retry
include: same as value, the default is empty, when exclude is also empty, all exceptions are retried
exclude: specify the exception not to retry, the default is empty, when include is also empty, all exceptions Retry both
maxAttemps: number of retries, default 3
backoff: retry compensation mechanism, default no

@Backoff annotation
delay: Retry after the specified delay
multiplier: Specify the multiple of the delay, such as delay=5000l, multiplier=2, the first retry is 5 seconds, the second time is 10 seconds, and the third time is 20 seconds

@Recover: When the retries reach the specified number of times, the annotated method will be called back, and log processing can be performed in this method. It should be noted that the callback will only be called when the exception occurs and the input parameter type is the same

Code example

@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
@Async //开启异步
@Override
public String helloSpringRetry(String msg) {
    log.info("helloSpringRetry invoke...");
    if (msg.equals("error")) {
        log.info("helloSpringRetry invoke error...");
        throw new RuntimeException("error");
    }
    log.info("helloSpringRetry invoke success...");
    return msg;
}

/**
 * 回调方法,注意:该回调方法与重试方法写在同一个实现类里面
 * @return
 */
@Recover
public String recover(Exception e) {
    log.info("记录日志...",e);
    throw new RuntimeException("重试失败");
}

Guess you like

Origin blog.csdn.net/kaihuishang666/article/details/106483014