背景
在项目开发中,有时候会出现接口调用失败,本身调用又是异步的,如果是因为一些网络问题请求超时,总想可以重试几次把任务处理掉。
一些RPC框架,比如dubbo都是有重试机制的,但是并不是每一个项目都会使用dubbo框架,常规的小项目有时候直接使用http进行不同项目之间的交互。
思路
使用spring aop和自定义注解来,建立一套重试机制。
根据切入点和自定义注解,来完成重试工作。
定义一个自定义注解
package com.it.ssm.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RetryProcess {
//重试的次数
int value() default 1;
}
定义一个切面
package com.it.ssm.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
@Aspect
@Component
public class AspectExceptionInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private AtomicInteger atomicInteger = new AtomicInteger(0);
@AfterThrowing(pointcut=("execution(* com.it.ssm..*(..)) && @annotation(com.it.ssm.annotation.RetryProcess)"))
public void tryAgain(JoinPoint point) {
try {
Object object = point.getTarget();
MethodSignature methodSignature = (MethodSignature) point.getSignature();
RetryProcess retryProcess = methodSignature.getMethod().getAnnotation(RetryProcess.class);
if (atomicInteger.intValue() < retryProcess.value()) {
int i = atomicInteger.incrementAndGet();
// 阻塞i秒后再进行重试,如果网络问题立即重试失败几率非常大所以建议阻塞一下再试
Thread.sleep(1000 * i);
logger.info("开始重试第" + i + "次");
MethodInvocationProceedingJoinPoint methodPoint = ((MethodInvocationProceedingJoinPoint) point);
// 再次调用方法
methodPoint.proceed();
}
} catch (Throwable throwable) {
// 捕获到异常后再次重试
tryAgain(point);
}
}
}
测试
@RetryProcess(value = 3)
@RequestMapping("/testException")
public void testException() throws Exception {
// 这里为了方便测试手动抛一个异常,实际此处应该是调用接口逻辑
throw new RuntimeException("测试重试异常");
}