第三方接口调用失败重试
规则
- 第三方接口调用失败后,相隔3秒后后重试;
- 若再次失败则相隔5秒重试,后续不再重试。
代码
@MyRetry
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRetry {
int retryTimes() default 0;
int[] retrySecond() default {};
}
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 {
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) {
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
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
private String code;
private String msg;
ThirdCallService
@MyRetry(retryTimes = 2, retrySecond = {3, 5})
ResponseResult push();
ThirdCallServiceImpl
@Override
public ResponseResult push() {
System.out.println("push()");
ResponseResult responseResult = new ResponseResult();
responseResult.setCode("-1");
responseResult.setMsg("连接超时,网络异常");
return responseResult;
}
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不为成功代号需要纳入重试机制。