重試機制實現總結

原生方法

@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;
}

動態代理

/**
 * @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代理

/**
 * @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();
    }
}

註解+AOP實現

@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引入

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

應用啓動類開啓retry

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

在指定方法上標記@Retryable來開啓重試

@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....");
}

在指定方法上標記@Recover來開啓重試失敗後調用的方法(注意,需跟重處理方法在同一個類中)

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

註解參數詳解

@Retryable註解:被註解的方法發生異常時會重試

value:指定發生的異常進行重試
include:和value一樣,默認空,當exclude也爲空時,所有異常都重試
exclude:指定異常不重試,默認空,當include也爲空時,所有異常都重試
maxAttemps:重試次數,默認3
backoff:重試補償機制,默認沒有

@Backoff註解
delay:指定延遲後重試
multiplier:指定延遲的倍數,比如delay=5000l,multiplier=2時,第一次重試爲5秒後,第二次爲10秒,第三次爲20秒

@Recover:當重試到達指定次數時,被註解的方法將被回調,可以在該方法中進行日誌處理。需要注意的是發生的異常和入參類型一致時纔會回調

代碼示例

@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("重試失敗");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章