前言
在微服务场景中,可能因为系统中网络抖动,导致调用超时或者失败, 按照我们分布式事务角度来看的话, 如果我们在业务中只调用了一次远程的服务查询(只去查询用户服务的某个信息),如果查询失败, 从而导致整个业务回滚, 这种代价是我们不想看到,所以我们就可以基于fegin的远程调用, 提高成功的可能性, 尽可能的避免回滚
来 我们上代码
首先基于AOP注解的方式, 定义切面
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {
Backoff backoff() default @Backoff(); //定义重试机制
int maxAttempt() default 3; //最大重试次数
Class<? extends Throwable>[] include() default {} ;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Backoff {
long delay() default 1000L; //第一次重试时间间隔
long maxDelay() default 0L; //最大延迟时间
double multiplier() default 0.0D;
}
@Aspect
@Slf4j
@Component
public class FeignRetryAspect {
@Around("@annotation(com.yomahub.tlog.example.feign.inteferce.FeignRetry)")
public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getCurrentMethod(joinPoint);
FeignRetry feignRetry = method.getAnnotation(FeignRetry.class);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));
return retryTemplate.execute(arg0 -> {
int retryCount = arg0.getRetryCount();
log.info("Sending request method: {}, max attempt: {}, delay: {}, retryCount: {}",
method.getName(),
feignRetry.maxAttempt(),
feignRetry.backoff().delay(),
retryCount
);
return joinPoint.proceed(joinPoint.getArgs());
},
context -> {
//重试失败后执行的代码
log.info("我在重试了{}次后,我最终还是失败了======", context.getRetryCount());
return "failed callback";
}
);
}
private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
if (feignRetry.backoff().multiplier() != 0) {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
return backOffPolicy;
} else {
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(feignRetry.backoff().delay());
return fixedBackOffPolicy;
}
}
private SimpleRetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
Map<Class<? extends Throwable>, Boolean> policyMap = new HashMap<>();
policyMap.put(RetryableException.class, true); // Connection refused or time out
policyMap.put(ClientException.class, true); // Load balance does not available (cause of RunTimeException)
if (feignRetry.include().length != 0) {
for (Class<? extends Throwable> t : feignRetry.include()) {
policyMap.put(t, true);
}
}
return new SimpleRetryPolicy(feignRetry.maxAttempt(), policyMap, true);
}
private Method getCurrentMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getMethod();
}
@FeignClient("tlog-logback-feign-provider")
public interface TLogFeignClient {
@RequestMapping(value = "hi",method = RequestMethod.GET)
@FeignRetry(maxAttempt = 6, backoff = @Backoff(delay = 500L, maxDelay = 20000L, multiplier = 4))
public String sayHello(@RequestParam(value = "name") String name);
}