SpringCloud Feign重試詳解


摘要: 今天在生產環境發生了數據庫進程卡死的現象,除了sql因為全量更新,沒加索引的原因,最主要還是我們的接口的服務器端接口出現問題了。忽視了更新接口的冪等性,以及調用方feign client的重試,導致接口重復執行。萬幸的是數據已經修復,花了幾個小時跟蹤feign和ribbon的源碼,把其原理徹底搞明白了。

      feign是netflix提供的服務間基於http的rpc調用框架,在spring cloud得到廣泛應用。默認情況下,一個feign client是在hystrix斷路器中執行,並利用ribbon進行軟負載選擇遠程target service,所以可以想象出一個feign client的層次架構是包裹的層次,hystrix控制整個rpc從調用到方法返回,而ribbon控制從選址到socket返回,關於它們的超時設置,請參考我上一篇博客:SpringCloud重試機制配置

      今天先不討論hystrix,僅從feign在spring cloud中應用容易踩到坑和從源碼debug的角度看執行過程。我們先來填坑,看看這個配置:

      這是ribbon在github wiki上的給我們的默認配置,OKToRetryOnAllOperations的意義是無論是請求超時或者socket read timeout都進行重試,

這個OKToRetryOnAllOperations=true我建議改成false或者不設,為什么?我們直接上源碼分析:

 

        這是feign初始化它的ribbon重試控制器,它的邏輯是如果設置了OKToRetryOnAllOperations這個參數為true,第一個if的構造函數就設置為true,這就比較危險了,如果接口是post或者put請求,這是進行修改操作,如果服務器長時間不返回,客戶端發生socket read timeout會進行重試,如果服務器接口沒做冪等性,這個后果自己想想。繼續看后面兩個判斷,得出的結論是:如果是Get請求設置為OKToRetryOnAllOperations=true不影響,因為只涉及到讀操作,如果是其他http方法,默認只會在socket還沒建立連接時進行重試,比如突然網絡抖動或者一台服務實例掛了,這是沒問題的,因為只保證了服務器端執行一次(還是建議涉及到修改的接口做好冪等性)。

    關於超時再提一下兩個配置ribbon.ConnectTimeout和ReadTimeout,根據自己服務調用情況,慎重進行設置,我的建議是ReadTimeout可以稍微設大點(同時注意hystrix線程池超時時間)。

下面我們分析下feign的執行過程和重試機制,下面這個圖是我簡易畫的,這是總體概覽

1、一個feign請求開始,通過動態代理的方式包裹了一層feign retryer邏輯,控制最外層的feign自身的重試機制:

2、continueOrPropagate是控制是否重試和跳出上層死循環的最終出口:

3、必要的ribbon設置,並調用真實執行邏輯

4、在AbstractLoadBalancerAwareClinet中執行,LoadBalancerCommand中控制ribbon選取server、重試、記錄執行狀態、封裝錯誤返回,這都是利用RXJava的觀察者模式來做的

  • 第一個catch控制ribbon請求的Exception

  •     第二個catch控制整個一輪ribbon重試(ribbon.MaxAutoRetries、ribbon.MaxAutoRetriesNextServer)下來,仍然異常。

  • 回到方法調用入口的catch,進行feign的retryer的邏輯,決定是整體再重試還是直接拋出異常跳出循環(默認是5次重試)

5、默認情況下,在feign.Client.Default的內部類里進行真實的http請求,默認是用Java的網絡api(這塊可以替換掉自己寫,比如使用:netty)

 

    總結下,注意我們的接口請求方式,設置合適的超時時間,OKToRetryOnAllOperations這個參數慎用。如果對網絡請求性能要求較高,可以在適當位置重寫源碼。


免責聲明!

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



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