feign源碼解讀

對於feign的接口請求失敗的重試配置可通過如下自定義配置文件實現(一般不建議配置)

<code>@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
/<code>

當然,也可使用默認的retry配置文件,下方是feign.Retryer的源碼

<code>// 類的全路徑是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;
}
/<code>

spring cloud中的feign整合了ribbon,但feign和ribbon都有重試功能,springcloud統一了兩者的行為,將feign的重試策略設置成永不重試,如果要使用feign的重試功能,只需要設置ribbon的重試配置即可,所以一般不建議配置feign的重試策略

feign源碼解讀

feign默認不配置重試策略是會重試的

ribbon默認配置如下

<code>ribbon:
# 同一實例最大重試次數,不包括首次調用。默認值為0
MaxAutoRetries: 0
# 同一個微服務其他實例的最大重試次數,不包括第一次調用的實例。默認值為1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允許重試。默認值為false
OkToRetryOnAllOperations: false
/<code>

默認情況下,GET方式請求無論是連接異常還是讀取異常,都會進行重試 非GET方式請求,只有連接異常時,才會進行重試

如此看來,如果同一個微服務只有一個實例是不會進行重試的,但事實並非如此分析一下源碼,feign的重試是在org.springframework.retry.support.RetryTemplate中的doExecute方法中進行中

<code>protected  T doExecute(RetryCallback retryCallback,RecoveryCallback 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);
}
......
}
/<code>

上方的canRetry是關鍵

feign源碼解讀

最後一行policy.canRetryNextServer是能否選擇下一個實例進行重試

feign源碼解讀

而lbContext.getRetryHandler().getMaxRetriesOnNextServer()就是變量retryNextServer

feign源碼解讀

feign源碼解讀

retryNextServer的值就是來源於MaxAutoRetriesNextServer,默認是1,所以canRetry在返回的是true,所以調用了二次解決辦法就是要進行如下配置

<code>ribbon:
# 同一實例最大重試次數,不包括首次調用。默認值為0
MaxAutoRetries: 0
# 同一個微服務其他實例的最大重試次數,不包括第一次調用的實例。默認值為1
MaxAutoRetriesNextServer: 0
# 是否所有操作(GET、POST等)都允許重試。默認值為false

OkToRetryOnAllOperations: false
/<code>

FeignRibbonClient的自動配置類

feign源碼解讀

可以看出,其默認使用LoadBalancerFeignClient的配置

feign源碼解讀

查看其DEFAULT_OPTIONS可知道默認連接超時時間是10s,讀取超時是6s

feign源碼解讀

默認的網絡請求框架是HttpURLConnection

feign源碼解讀

如要更換相應的網絡請求框架,只需要添加相應的pom依賴即可

feign源碼解讀

查看負載均衡怎麼做的,查看executeWithLoadBalancer

feign源碼解讀

查看其submit任務

feign源碼解讀

其方法selectServer就是負載均衡的關鍵

feign源碼解讀

Feign的流程如下

  1. 通過@EnableFeignClients開啟feign
  2. 根據要遠程調用的接口添加@FeignClient
  3. 程序掃描特定包下的FeignClient註解並注入Ioc容器
  4. 當feign接口被調用時,通過jdk代理生成相應的RequestTemplate
  5. 根據RequestTemplate生成相應的Request
  6. Request交給類Client去調用,Client可以是HttpClient,Okhttp或HttpUrlConnection
  7. 最後Client被封裝到LoadBalanceClient,這個類結合ribbon實現負載均衡


分享到:


相關文章: