Método nativo
@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;
}
Proxy dinâmico
/**
* @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);
}
}
Agente 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();
}
}
Implementação de anotação + 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;
}
}
Nova tentativa de primavera
introdução pom
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
A classe de inicialização do aplicativo abre nova tentativa
@EnableRetry
public class Application {
.......
}
Marque @Retryable no método especificado para permitir uma nova tentativa
@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....");
}
Marque @Recover no método especificado para permitir que o método seja chamado após a falha da nova tentativa (observe que ele precisa estar na mesma classe do método de reprocessamento)
@Recover
public void recover(A异常 e) {
// ... do something
}
@Recover
public void recover(B异常 e) {
// ... do something
}
Explicação detalhada dos parâmetros de anotação
Anotação @Retryable: o método anotado tentará novamente quando ocorrer uma exceção
valor: especifique a ocorrência da exceção para nova tentativa
include: igual ao valor, o padrão é vazio, quando a exclusão também está vazia, todas as exceções são
tentadas novamente exclude: especifique a exceção para não tentar novamente, o padrão é vazio, quando a inclusão também está vazia, todas as exceções Tentar novamente
maxAttemps: número de tentativas, padrão 3
recuo: mecanismo de compensação de nova tentativa, padrão não
@Backoff annotation
delay: Repetir após o
multiplicador de atraso especificado : especifique o múltiplo do atraso, como delay = 5000l, multiplier = 2, a primeira tentativa é 5 segundos, a segunda vez é 10 segundos e a terceira vez é 20 segundos
@Recover: quando as novas tentativas atingirem o número especificado de vezes, o método anotado será chamado de volta, e o processamento do log pode ser executado neste método. Deve-se notar que o callback só será chamado quando ocorrer a exceção e o tipo de parâmetro de entrada for o mesmo
Exemplo de código
@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("重试失败");
}