原生方法
@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("重试失败");
}