一、概述
在日常开发中,接口调用异常是经常会遇到的,任何接口都会有不同情况的异常情况,对于可以重试的接口,为了避免偶发异常造成的接口的不可用,重试机制就非常有必要了。
二、常用重试组件
1、guava-retrying
2、spring retry
三、guava-retrying使用介绍
1、maven引入
<dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>
2、demo
public static void main(String[] args) throws Exception { Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.equalTo(false)) .retryIfException() .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS)) .withStopStrategy(StopStrategies.stopAfterAttempt(5)) .withRetryListener(new MyRetryListener<>()) .build(); try { retryer.call(()->{ //call something return true; }); } catch (Exception e) { e.printStackTrace(); } }
3、基于AOP方式使用
3.1 定义注解类
import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * guava retrying 注解使用 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface GuavaRetrying { //异常 Class[] exceptionClass() default {}; //重试次数 int attemptNumber() default 0; //等待时间 long waitStrategySleepTime() default 0; //持续时间; 期间 long duration() default 0; }
3.2 AOP类
import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Service; @Aspect @Service public class GuavaRetryingAspect { @Around(value = "@annotation(com.smart.service.aop.GuavaRetrying)") public Object monitorAround(ProceedingJoinPoint pjp) throws Throwable { Method method; if (pjp.getSignature() instanceof MethodSignature) { MethodSignature signature = (MethodSignature) pjp.getSignature(); method = signature.getMethod(); } else { return null; } GuavaRetrying annotation = method.getDeclaredAnnotation(GuavaRetrying.class); //重试时间,重试次数 if (annotation.duration() <= 0 && annotation.attemptNumber() <= 1) { return pjp.proceed(); } RetryerBuilder builder = RetryerBuilder.newBuilder(); //重试次数 if (annotation.attemptNumber() > 0) { builder.withStopStrategy(StopStrategies.stopAfterAttempt(annotation.attemptNumber())); } //退出策略 if (annotation.duration() > 0) { builder.withStopStrategy(StopStrategies.stopAfterDelay(annotation.duration(), TimeUnit.MILLISECONDS)); } //重试间间隔等待策略 if (annotation.waitStrategySleepTime() > 0) { builder.withWaitStrategy(WaitStrategies.fixedWait(annotation.waitStrategySleepTime(), TimeUnit.MILLISECONDS)); } //停止重试的策略 if (annotation.exceptionClass().length > 0) { for (Class retryThrowable : annotation.exceptionClass()) { if (retryThrowable != null && Throwable.class.isAssignableFrom(retryThrowable)) { builder.retryIfExceptionOfType(retryThrowable); } } } return builder.build().call(() -> { try { return pjp.proceed(); } catch (Throwable throwable) { throw new Exception(throwable); } }); } }
3.3 使用实例
@GuavaRetrying(exceptionClass = RemoteException.class, attemptNumber = 2) public Product getProductById(Long pId) { try { Product product = productService.getProductById(pId); return result; } catch (Exception e) { throw new RemoteException(e, null); } }