前言
前面三篇文章已经分析了Feign的通信Client
组件,Client是Rpc框架中最核心关键的能力,通过Client发起远程通信。
# OpenFeign第一个可扩展组件通信Client详解
# OpenFeign最核心组件LoadBalancerFeignClient详解
# OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
市面上的Rpc框架在核心通信能力的基础上,有很多服务治理的组件,OpenFeign也一样,今天主角是重试组件,我们看看OpenFeign是如何实现重试的。
重试组件入口
OpenFeign执行远程请求是在feign.SynchronousMethodHandler#invoke
,重试组件也是在这里嵌入的,我们看看具体实现
//调用远程入口
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Request.Options options = this.findOptions(argv);
Retryer retryer = this.retryer.clone();
//死循环,如果成功或者重试结束就返回
while(true) {
try {
//通过client发起通信
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
//处理RetryableException异常
RetryableException e = var9;
try {
//重试组件生效,判断是否重试
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
}
}
}
发起请求的地方捕获了RetryableException
异常,并且执行了重试的逻辑。 Feign提供了两个重试实现
1、feign.Retryer.Default
2、feign.Retryer#NEVER_RETRY
OpenFeign默认使用不重试
从openfeign的装配类FeignClientsConfiguration
我们可以看出 在OpenFeign中,默认使用的是NEVER_RETRY,不重试
@Configuration
public class FeignClientsConfiguration {
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
}
OpenFeign默认重试组件分析
feign.Retryer.Default
是OpenFeign实现了重试能力的重试组件,我们看下他是如何实现的
public static class Default implements Retryer {
private final int maxAttempts;
private final long period;
private final long maxPeriod;
int attempt;
long sleptForMillis;
public Default() {
this(100L, TimeUnit.SECONDS.toMillis(1L), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
protected long currentTimeMillis() {
return System.currentTimeMillis();
}
public void continueOrPropagate(RetryableException e) {
//已经超过最大重试次数,不重试了,异常往上抛
//attempt默认是1
if (this.attempt++ >= this.maxAttempts) {
throw e;
} else {
long interval;
//根据上次重试时间计算间隔时间
if (e.retryAfter() != null) {
interval = e.retryAfter().getTime() - this.currentTimeMillis();
//间隔时间是否超过最大重试间隔时间
if (interval > this.maxPeriod) {
interval = this.maxPeriod;
}
if (interval < 0L) {
return;
}
} else {
//获取下次重试间隔时间
interval = this.nextMaxInterval();
}
try {
//睡眠重试间隔,再发起重试
Thread.sleep(interval);
} catch (InterruptedException var5) {
Thread.currentThread().interrupt();
throw e;
}
this.sleptForMillis += interval;
}
}
long nextMaxInterval() {
//计算下次重试间隔,按照1.5为底的指数递增
long interval = (long)((double)this.period * Math.pow(1.5, (double)(this.attempt - 1)));
return interval > this.maxPeriod ? this.maxPeriod : interval;
}
public Retryer clone() {
return new Default(this.period, this.maxPeriod, this.maxAttempts);
}
}
1、默认重试是捕获RetryableException异常,如果是RetryableException异常则执行重试逻辑。
2、每次重试间隔按照底数是1.5的指数级递增
自定义重试组件
我们可以自己实现重试组件feign.Retryer
接口,也可以使用OpenFeign自带的重试组件feign.Retryer.Default
,假如我们使用自带的重试组件,我们只要定义一个配置类,配置Retryer的Bean,覆盖默认的Retryer.NEVER_RETRY
;
下方是配置每次重试间隔是100ms,最大重试间隔是1s, 重试次数是2次
@Component
public class CustomFeignConfig {
public Integer retryPeriod = 100;
public Integer retryMaxPeriod = 1000;
public Integer maxRetryAttempts = 2;
//自定义重试组件,覆盖OpenFeign默认的Retryer.NEVER_RETRY;
@Bean
public Retryer retryer() {
return new Retryer.Default(retryPeriod, retryMaxPeriod, maxRetryAttempts);
}
}
通过上面的配置类,OpenFeign就具备重试功能了,请注意虽然maxRetryAttempts
配置是2,但是只会重试1次,2次包括本身调用那一次
。也就是说,如果想除去本身调用那次外重试2次,则maxRetryAttempts
需要配置为3。
总结
1、OpenFeign提供了重试组件,默认是不重试的
2、feign.Retryer.Default
的实际重试次数是maxAttempts-1
OpenFeign重试组件分析完了,对你有没有帮助呢?