feign源碼解讀

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

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

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

// 類的全路徑是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是關鍵 this


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


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


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

ribbon:
  # 同一實例最大重試次數,不包括首次調用。默認值爲0
  MaxAutoRetries: 0
  # 同一個微服務其餘實例的最大重試次數,不包括第一次調用的實例。默認值爲1
  MaxAutoRetriesNextServer: 0
  # 是否全部操做(GET、POST等)都容許重試。默認值爲false
  OkToRetryOnAllOperations: false

FeignRibbonClient的自動配置類


能夠看出,其默認使用LoadBalancerFeignClient的配置


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


默認的網絡請求框架是HttpURLConnection


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


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


查看其submit任務


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

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實現負載均衡
相關文章
相關標籤/搜索