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