第三方接口调用失败重试
规则
- 第三方接口调用失败后,相隔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 {
// 接口方法上是否有@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
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; // 省略getter/setter方法
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不为成功代号需要纳入重试机制。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227906.html原文链接:https://javaforall.net
