SpringCloud Fegin超時(shí)重試源碼

springCloud中最重要的就是微服務(wù)之間的調(diào)用,因?yàn)榫W(wǎng)絡(luò)延遲或者調(diào)用超時(shí)會(huì)直接導(dǎo)致程序異常,因此超時(shí)的配置及處理就至關(guān)重要。

在開(kāi)發(fā)過(guò)程中被調(diào)用的微服務(wù)打斷點(diǎn)發(fā)現(xiàn)會(huì)又多次重試的情況,測(cè)試環(huán)境有的請(qǐng)求響應(yīng)時(shí)間過(guò)長(zhǎng)也會(huì)出現(xiàn)多次請(qǐng)求,網(wǎng)上查詢了配置試了一下無(wú)果,決定自己看看源碼。
本人使用的SpringCloud版本是Camden.SR3。

微服務(wù)間調(diào)用其實(shí)走的是http請(qǐng)求,debug了一下默認(rèn)的ReadTimeout時(shí)間為5s,ConnectTimeout時(shí)間為2s,我使用的是Fegin進(jìn)行微服務(wù)間調(diào)用,底層用的還是Ribbon,網(wǎng)上提到的配置如下

ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1

超時(shí)時(shí)間是有效的但是重試的次數(shù)無(wú)效,如果直接使用ribbon應(yīng)該是有效的。

下面開(kāi)始測(cè)試:
在微服務(wù)B中建立測(cè)試方法,sleep 8s 確保請(qǐng)求超時(shí)

    public Integer testee(){
        try {
            Thread.sleep(8000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 9;
    }

在微服務(wù)A中使用fegin調(diào)用此方法時(shí)看到有異常

看到在SynchronousMethodHandler中請(qǐng)求的方法

    Request request = targetRequest(template);
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }
    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
      response.toBuilder().request(request).build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
     //出現(xiàn)異常后拋出RetryableException
      throw errorExecuting(request, e);
    }

出現(xiàn)異常后調(diào)用 throw errorExecuting(request, e) 拋出異常

在調(diào)用executeAndDecode的地方catch

@Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } catch (RetryableException e) {
         //重試的地方
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

retryer.continueOrPropagate(e); 這句就是關(guān)鍵繼續(xù)跟進(jìn)

public void continueOrPropagate(RetryableException e) {
        //maxAttempts是構(gòu)造方法傳進(jìn)來(lái)的大于重試次數(shù)拋出異常,否則繼續(xù)循環(huán)執(zhí)行請(qǐng)求
      if (attempt++ >= maxAttempts) {
        throw e;
      }
     ....

默認(rèn)的Retryer構(gòu)造器

 public Default() {
      this(100, SECONDS.toMillis(1), 5);
    }

第一個(gè)參數(shù)period是請(qǐng)求重試的間隔算法參數(shù),第二個(gè)參數(shù)maxPeriod 是請(qǐng)求間隔最大時(shí)間,第三個(gè)參數(shù)是重試的次數(shù)。算法如下:

      long interval = (long) (period * Math.pow(1.5, attempt - 1));
      return interval > maxPeriod ? maxPeriod : interval;
    }

我們能否改寫(xiě)參數(shù)呢?我們?cè)倏纯碨pringCloud的文檔中關(guān)于Retry的配置


新建一個(gè)配置類(lèi)

@Configuration
public class FeginConfig {
    @Bean
    public Retryer feginRetryer(){
        Retryer retryer = new Retryer.Default(100, SECONDS.toMillis(10), 3);
        return retryer;
    }
}

在feginClient是加入configuration的配置

@FeignClient(value = "fund-server",fallback = FundClientHystrix.class,configuration = FeginConfig.class)
public interface FundClient 

重啟重試,只調(diào)用了一次,F(xiàn)egin重試次數(shù)解決。
我們?cè)倏纯凑?qǐng)求超時(shí)這里的參數(shù)

@Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);
                     //請(qǐng)求參數(shù)
            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                    requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }

其中ReadTimeout 和 ConnectTimeout 讀取的就是ribbon的配置,再來(lái)看一眼

ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1

如果想覆蓋ribbon的超時(shí)設(shè)置可以在剛剛寫(xiě)的FeginConfig里注入下面的bean

    public Request.Options feginOption(){
        Request.Options option = new Request.Options(7000,7000);
        return option;
    }

總結(jié):使用開(kāi)源的東西在弄不清問(wèn)題出在哪時(shí)最好能看看源碼,對(duì)原理的實(shí)現(xiàn)以及自己的編碼思路都有很大的提升。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,653評(píng)論 19 139
  • 1. 功能介紹 1.1. Volley Volley 是 Google 推出的 Android 異步網(wǎng)絡(luò)請(qǐng)求框架和...
    愛(ài)碼士平頭哥閱讀 1,926評(píng)論 0 9
  • Xutils3.0技術(shù)分享1.這個(gè)技術(shù)分享的目的1.首先要讓大家了解Xutil3.0是什么Xtuils3.0的前身...
    wodezhuanshu閱讀 3,378評(píng)論 5 9
  • Address:https://www.zybuluo.com/XiangZhou/note/208532 Exp...
    天蠍蒗漫閱讀 11,626評(píng)論 2 55
  • 5. 最佳實(shí)踐 好了終于要點(diǎn)講自己的東西了,有點(diǎn)小激動(dòng)。下面這些僅表示個(gè)人觀點(diǎn),非一定之規(guī),各位看官按需取用,有說(shuō)...
    SnowDragonYY閱讀 2,492評(píng)論 4 36

友情鏈接更多精彩內(nèi)容