【Feign/Ribbon】記錄一次生產上的SpringCloudFeign的重試問題


  在上周在的微供有數項目中(數據產品),需要對接企業微信中第三方應用,在使用Feign的去調用微服務的用戶模塊用微信的code獲取access_token以及用戶工廠信息時出現Feign重試超時報錯的情況,通過此篇文章記錄問題解決的過程。

  一.問題重現:

    1.SpringCloud部分依賴如下

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

    2.微信相關的接口文檔:

    前端通過企業id,配置好回調域名之后,調用微信的Api去獲取code

    見文檔https://work.weixin.qq.com/api/doc/90000/90135/91022

    注意:

      code只能用一次,見文檔,因此獲取到的access_token需要緩存起來,項目中是緩存到redis中的,用於后續的消息推送等等功能

      

 

 

 

     3.請求流程圖

      

 

 

     

   二.原因分析

    1.整個請求的鏈路中,階段2是feign請求的位置,但是yml配置文件中並沒有配置feign,因此可以斷定feign使用的默認的配置,問題發生時,查看feign的文檔發現,feign重試默認超時時間是1s,

    因此現在重新配置feign的超時時間,現有feign的配置如下

feign:
  client:
    config:
      organization:
        connectTimeout: 5000
        readTimeout: 5000

    其實organization表示的就是feign所調用的服務名稱

    connectTimeout表示建立請求連接的連接的時間(這里面包括獲取請求eureka中保存的服務列表-推測)

    readTimeout表示連接建立以后請求調用的時間

 

    2.在上述配置中,通過查看organization和data服務的請求日志,發現請求都能順利的建立,但是當階段三去請求微信的接口一旦延遲,則會觸發feign的重試進行第二次調用;

    由於階段三請求微信的接口並不是沒有調用,而是由於網絡或者其他原因導致的微信沒有響應,但是code又已經被消費了,當階段二攜帶同樣的code去調用微信的接口,這時就會出現

    code已經被消費

 

    3.此時有另外一個問題就是,項目中的服務都是單實例部署,springcloud組件中feign和ribbon都有重試的功能,

    Spring Cloud中Feign整合了Ribbon,但Feign和Ribbon都有重試的功能,Spring Cloud為了統一兩者的行為,在C版本以后,將Feign的重試策略默認設置為 feign.Retryer#NEVER_RETRY(即永不重試)

    因此Feign的調用本質還是通過ribbon去實現

   三,相關配置測試

    經過測試后,發現feign和ribbon的配置都能實現超時熔斷
    版本號:SpringCloud  Greenwich.SR1
    配置一:
     只配置feign相關,即feign覆蓋了ribbon默認的超時時間配置,但是注意,該配置會觸發ribbon的重試,
feign:
  client:
    config:
      eureka-client:
        connectTimeout: 1000
        readTimeout: 1000

    配置二:

    只配置ri'bbon

    注意 這里有個坑MaxAutoRetriesNextServer這個參數如果不配置為0,即使在單實例部署的情況下,仍然會發生重試1次,因此如果不想發生重試,則需要手動配置

    MaxAutoRetriesNextServer=0和MaxAutoRetries=0

ribbon:
  ReadTimeout: 4000
  ConnectionTimeout: 4000
  OkToRetryOnAllOperations: true
  MaxAutoRetriesNextServer: 0 # 當前實例全部失敗后可以換1個實例再重試,
  MaxAutoRetries: 1 # 在當前實例只重試2次 

    配置三:

    feign和ribbon都不配置,注意,經過測試后發現這里使用的是ribbon默認的超時配置,配置如下:

    MaxAutoRetriesNextServer=1   

    MaxAutoRetries=0

public LoadBalancerContext(ILoadBalancer lb) {
        this.clientName = "default";
        this.maxAutoRetriesNextServer = 1;
        this.maxAutoRetries = 0;
        this.defaultRetryHandler = new DefaultLoadBalancerRetryHandler();
        this.okToRetryOnAllOperations = DefaultClientConfigImpl.DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS;
        this.lb = lb;
    }

    版本號:SpringCloud   Dalston.SR1 與 Greenwich.SR1的測試結論一致  

    注意:  Dalston.SR1  ribbon組件默認的超時時間

    public static final int DEFAULT_READ_TIMEOUT = 5000;

public static final int DEFAULT_CONNECT_TIMEOUT = 2000;

     Greenwich.SR1 ribbon組件默認的超時間

    public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_READ_TIMEOUT = 1000;

 

 

      

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM