文章目錄
-
- @[toc]
-
- 第三方接口調用失敗重試
-
- 規則
- 代碼
-
- `@MyRetry`
- `MyRetryFactory`
- `MyRetryTemplate`
- `ResponseResult`
- `ThirdCallService`
- `ThirdCallServiceImpl`
- `Tester`
-
- 結果
- 總結
- @[toc]
-
- 第三方接口調用失敗重試
-
- 規則
- 代碼
-
- `@MyRetry`
- `MyRetryFactory`
- `MyRetryTemplate`
- `ResponseResult`
- `ThirdCallService`
- `ThirdCallServiceImpl`
- `Tester`
-
- 結果
- 總結
第三方接口調用失敗重試
規則
- 第三方接口調用失敗後,相隔3秒後後重試;
- 若再次失敗則相隔5秒重試,後續不再重試。
代碼
@MyRetry
@MyRetry
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRetry {
int retryTimes() default 0;
int[] retrySecond() default {};
}
MyRetryFactory
MyRetryFactory
public class MyRetryFactory {
public static <T> T getRetryServiceProxy(T realObj) {
Class<?>[] realIntfs = realObj.getClass().getInterfaces();
Object proxyInstance = Proxy.newProxyInstance(MyRetryFactory.class.getClassLoader(), realIntfs,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 接口方法上是否有@MyRetry
if (method.isAnnotationPresent(MyRetry.class)) {
MyRetry myRetry = method.getDeclaredAnnotation(MyRetry.class);
int retryTimes = myRetry.retryTimes();
int[] retrySeconds = myRetry.retrySecond();
MyRetryTemplate myRetryTemplate = new MyRetryTemplate() {
@Override
public Object retry() throws Exception {
Object obj = method.invoke(realObj, args);
if (obj instanceof ResponseResult) {
// 網絡異常,第三方接口也會傳回結果,判斷code是否等于0,決定是否重試
ResponseResult responseResult = (ResponseResult) obj;
if (responseResult == null || (!"0".equals(responseResult.getCode()))) {
if (responseResult == null) {
throw new RuntimeException("接口傳回對象為空");
} else {
throw new RuntimeException(responseResult.getMsg());
}
}
}
return obj;
}
}.setRetryTimes(retryTimes).setRetrySeconds(retrySeconds);
// 先執行方法一次,再異步重試
try {
return myRetryTemplate.executeOnce();
} catch(Exception e) {
myRetryTemplate.executeAsync();
}
return null;
} else {
return method.invoke(realObj, args);
}
};
});
return (T) proxyInstance;
}
}
MyRetryTemplate
MyRetryTemplate
public abstract class MyRetryTemplate {
private int retryTimes = 0;
private int[] retrySeconds = {};
public abstract Object retry() throws Exception;
public Object executeOnce() throws Exception {
System.out.println("第一次執行...");
return retry();
}
public Object execute() {
System.out.println("重試" + retryTimes + "次-分别相隔" + Arrays.toString(retrySeconds) + "秒");
for (int i = 0; i < retryTimes; i++) {
try {
System.out.println(retrySeconds[i] + "s後準備第[" + (i + 1) + "]次重試!");
Thread.sleep(1000 * retrySeconds[i]);
return retry();
} catch (Exception e) {
System.out.println("重試失敗:" + e);
}
}
return null;
}
public void executeAsync() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
execute();
}
});
thread.start();
}
public MyRetryTemplate setRetryTimes(int retryTimes) {
this.retryTimes = retryTimes;
return this;
}
public MyRetryTemplate setRetrySeconds(int[] retrySeconds) {
this.retrySeconds = retrySeconds;
return this;
}
}
ResponseResult
ResponseResult
private String code;
private String msg;
// 省略getter/setter方法
ThirdCallService
ThirdCallService
@MyRetry(retryTimes = 2, retrySecond = {3, 5})
ResponseResult push();
ThirdCallServiceImpl
ThirdCallServiceImpl
@Override
public ResponseResult push() {
System.out.println("push()");
ResponseResult responseResult = new ResponseResult();
responseResult.setCode("-1");
responseResult.setMsg("連接配接逾時,網絡異常");
return responseResult;
}
Tester
Tester
ThirdCallService thirdCallService = new ThirdCallServiceImpl();
ThirdCallService thirdCallServiceProxy = MyRetryFactory.getRetryServiceProxy(thirdCallService);
thirdCallServiceProxy.push();
System.out.println("=============================");
結果
第一次執行...
push()
=============================
重試2次-分别相隔[3, 5]秒
3s後準備第[1]次重試!
push()
重試失敗:java.lang.RuntimeException: 連接配接逾時,網絡異常
5s後準備第[2]次重試!
push()
重試失敗:java.lang.RuntimeException: 連接配接逾時,網絡異常
總結
1. 符合模闆方法模式。因為要重試幾次,是以需要循環,循環總體邏輯一緻,但是要調用的外部接口有很多。
2. 第一次正常調用接口傳回結果,失敗重試n次采用異步調用。
3. 采用JDK的動态代理。調用每個外部接口時,統一交由代理類實作總體重試規則代碼。
4. 接口标注注解,來區分外部接口是否需要重試。
代理類可依照該注解,分别執行需要重試和不需要重試的邏輯,做到統一區分。
5. 外部接口一般異常(如網絡異常)傳回的對象也會有值(code,msg),code不為成功代号需要納入重試機制。