對於feign的介面請求失敗的重試配置可通過如下自定義配置文件實現(一般不建議配置) 當然,也可使用預設的retry配置文件,下方是feign.Retryer的源碼 spring cloud中的feign整合了ribbon,但feign和ribbon都有重試功能,springcloud統一了兩者的行 ...
對於feign的介面請求失敗的重試配置可通過如下自定義配置文件實現(一般不建議配置)
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
當然,也可使用預設的retry配置文件,下方是feign.Retryer的源碼
// 類的全路徑是feign.Retryer
public Default() {
// 預設是重試的間隔是100ms,最大重試間隔是1秒,最大重試次數是5次
this(100, SECONDS.toMillis(1), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
// 如果重試的次數大於最大重試次數,拋異常
if (attempt++ >= maxAttempts) {
throw e;
}
long interval;
if (e.retryAfter() != null) {
interval = e.retryAfter().getTime() - currentTimeMillis();
// 如果重試間隔大於最大間隔,則取最大間隔
if (interval > maxPeriod) {
interval = maxPeriod;
}
if (interval < 0) {
return;
}
} else {
// 如果重試間隔沒有明確,則調用nextMaxInterval獲取
interval = nextMaxInterval();
}
try {
// sleep一定時間後再去重試
Thread.sleep(interval);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
// sleptForMillis變數是總的重試間隔
sleptForMillis += interval;
}
/**
* 下一次重試的間隔,間隔時間每一次重試都是1.5倍遞增,直到最大間隔
**/
long nextMaxInterval() {
long interval = (long) (period * Math.pow(1.5, attempt - 1));
return interval > maxPeriod ? maxPeriod : interval;
}
spring cloud中的feign整合了ribbon,但feign和ribbon都有重試功能,springcloud統一了兩者的行為,將feign的重試策略設置成永不重試,如果要使用feign的重試功能,只需要設置ribbon的重試配置即可,所以一般不建議配置feign的重試策略
feign預設不配置重試策略是會重試的
ribbon預設配置如下
ribbon:
# 同一實例最大重試次數,不包括首次調用。預設值為0
MaxAutoRetries: 0
# 同一個微服務其他實例的最大重試次數,不包括第一次調用的實例。預設值為1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允許重試。預設值為false
OkToRetryOnAllOperations: false
預設情況下,GET方式請求無論是連接異常還是讀取異常,都會進行重試
非GET方式請求,只有連接異常時,才會進行重試
如此看來,如果同一個微服務只有一個實例是不會進行重試的,但事實並非如此
分析一下源碼,feign的重試是在org.springframework.retry.support.RetryTemplate中的doExecute方法中進行中
protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,RecoveryCallback<T> recoveryCallback, RetryState state) throws E, ExhaustedRetryException {
......
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
// Reset the last exception, so if we are successful
// the close interceptors will not think we failed...
lastException = null;
return retryCallback.doWithRetry(context);
}
......
}
上方的canRetry是關鍵
最後一行policy.canRetryNextServer是能否選擇下一個實例進行重試
而lbContext.getRetryHandler().getMaxRetriesOnNextServer()就是變數retryNextServer
retryNextServer的值就是來源於MaxAutoRetriesNextServer,預設是1,所以canRetry在返回的是true,所以調用了二次
解決辦法就是要進行如下配置
ribbon:
# 同一實例最大重試次數,不包括首次調用。預設值為0
MaxAutoRetries: 0
# 同一個微服務其他實例的最大重試次數,不包括第一次調用的實例。預設值為1
MaxAutoRetriesNextServer: 0
# 是否所有操作(GET、POST等)都允許重試。預設值為false
OkToRetryOnAllOperations: false
FeignRibbonClient的自動配置類
可以看出,其預設使用LoadBalancerFeignClient的配置
查看其DEFAULT_OPTIONS可知道預設連接超時時間是10s,讀取超時是6s
預設的網路請求框架是HttpURLConnection
如要更換相應的網路請求框架,只需要添加相應的pom依賴即可
查看負載均衡怎麼做的,查看executeWithLoadBalancer
查看其submit任務
其方法selectServer就是負載均衡的關鍵
Feign的流程如下
- 通過@EnableFeignClients開啟feign
- 根據要遠程調用的介面添加@FeignClient
- 程式掃描特定包下的FeignClient註解並註入Ioc容器
- 當feign介面被調用時,通過jdk代理生成相應的RequestTemplate
- 根據RequestTemplate生成相應的Request
- Request交給類Client去調用,Client可以是HttpClient,Okhttp或HttpUrlConnection
- 最後Client被封裝到LoadBalanceClient,這個類結合ribbon實現負載均衡