0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Feign的超時時間如何設置呢?

jf_ro2CN3Fa ? 來源:三友的java日記 ? 2023-11-15 10:22 ? 次閱讀

今天來聊一聊前段時間看到的一個面試題,也是在實際項目中需要考慮的一個問題,Feign 的超時時間如何設置?

Feign 的超時時間設置方式并不固定,它取決于 Feign 在項目中是如何使用的,不同的使用方式,超時時間設置方式也不大相同,甚至還可能有坑。

前置知識

由于文章會涉及到 Feign 的底層知識,如果不懂點 Feign 的基本概念的話,后面就看不下去了。

所以為了方便不了解 Feign 的小伙伴也能夠讀得懂文章,這里我就簡單地說說 Feign 的原理,點到為止,雖然不深入,但足夠應付這篇文章了。

Feign 的作用

在項目中,我們經常需要調用第三方提供的 Http 接口,此時我們就可以使用一些 Http 框架來實現,比如 HttpClient。

publicclassHttpClientDemo{

publicstaticvoidmain(String[]args)throwsException{
//創(chuàng)建一個HttpClient
HttpClienthttpClient=HttpClientBuilder.create().build();

//構建一個get請求
HttpGethttpGet=newHttpGet("http://192.168.100.1:8080/order/1");

//發(fā)送請求,獲取響應
HttpResponsehttpResponse=httpClient.execute(httpGet);
HttpEntityhttpEntity=httpResponse.getEntity();

//讀出響應值
Stringresponse=EntityUtils.toString(httpEntity);

System.out.println("Response:"+response);
}

}

如果項目中只有一兩個這種第三方接口這樣寫還行,但是一旦這種三方接口過多的話,每次都得這樣組裝參數,發(fā)送請求,寫一堆同樣的代碼,就顯然很麻煩了。

所以為了簡化發(fā)送 Http 請求的開發(fā),減少重復代碼,Feign 就出現了。

Feign 是一個聲明式的 Http 框架

當你需要調用 Http 接口時,你需要聲明一個接口,加一些注解就可以了。而像組裝參數、發(fā)送 Http 請求等重復性的工作都交給 Feign 來完成。

Feign 的原理

雖然有了接口,但是僅僅有接口是不夠的,因為接口又不能創(chuàng)建對象,我們得需要對象。

Feign 為了方便我們?yōu)榻涌趧?chuàng)建對象,提供的 Feign.Builder 這個內部類。

8d711d92-82d4-11ee-939d-92fbcf53809c.png

這個類的作用就是解析接口的上的注解,為接口生成一個動態(tài)代理對象,后面通過這個代理對象就可以發(fā)送請求了。

這個內部類有很多屬性,這些屬性都是 Feign 的核心組件。

在這些核心的組件中有一個叫 Client 的,上圖中我圈出來了。

8da0279a-82d4-11ee-939d-92fbcf53809c.png

這個 Client 類劃個重點,非常非常重要,本文討論的東西跟他有密切關系。

它只有一個方法 Response execute (Request request, Options options)

方法的第一個參數 Request 就是封裝了 http 請求的 url、請求方法,請求頭、請求體之類的參數。

8db7c242-82d4-11ee-939d-92fbcf53809c.png

第二個參數 Options 就是本文的主題,封裝了超時時間。

8dcc3a2e-82d4-11ee-939d-92fbcf53809c.png

返回值 Response就是封裝了一些響應碼 status、響應頭之類的。

8ddfaed8-82d4-11ee-939d-92fbcf53809c.png

所以通過方法的參數和返回值也可以猜出來,這個 Client 作用是用來組裝 HTTP 請求參數,發(fā)送 HTTP 請求的。并且 HTTP 請求超時時間是根據傳給 Client 的 Options 參數來決定的。

8df27d92-82d4-11ee-939d-92fbcf53809c.png

Feign 單獨使用時超時時間設置

Feign 本身就是一個 HTTP 客戶端,可獨立使用,Feign 提供了兩種超時時間設置方式。

1、通過 Feign.Builder 設置

前面提到,Feign.Builder 的作用是為接口的動態(tài)代理對象的。

Feign.Builder 里面有很多屬性,其中就有關于超時時間的屬性 Options。

8e04ed10-82d4-11ee-939d-92fbcf53809c.png

如果你不設置,那么超時時間就是默認的。

8e0f6c36-82d4-11ee-939d-92fbcf53809c.png

默認的就是連接超時 10s,讀超時 60s。

所以可以通過設置 Feign.Builder 中的 options 來設置超時時間。

來個 demo

環(huán)境準備,就是一個簡單的 SpringBoot 項目,引入一個 Feign 的依賴。

這里演示的是 Feign 原生的使用方式,脫離于 SpringCloud 環(huán)境,所以 Spring 的那些 @GetMappring 就不支持了,改用 Feign 本身提供的注解。

測試代碼

這里面的請求路徑都是不存在的,因為我們只關心傳給 Client 的 Options 參數值。

Client 在我們不設置的時候,就用默認的實現 Client.Default。

8e208a48-82d4-11ee-939d-92fbcf53809c.png

斷點打到 execute 方法的實現,運行,走起。

8e39bab8-82d4-11ee-939d-92fbcf53809c.png

結果就是我們設置的 5s。

2、在接口方法參數設置

除了在通過 Feign.Builder 時設置之外,Feign 還支持在接口的方法參數上設置。

此時你只需要在接口的方法上加一個 Options 類型的參數。

@RequestLine("GET/user/{userId}")
UserqueryUser(@Param("userId")IntegeruserId,Request.Optionsoptions);

這樣在傳參數時就可以設置超時時間了。

Useruser=client.queryUser(123,newRequest.Options(3,TimeUnit.SECONDS,3,TimeUnit.SECONDS,true));

同樣地,debug 就可以看見我們設置的 3s 了。

8e4f74d4-82d4-11ee-939d-92fbcf53809c.png

這兩種設置超時時間的主要區(qū)別就是方法參數設置超時時間的優(yōu)先級高于 Feign.Builder 設置的超時時間。

用一張圖來總結一下上面的關系。

8e6506be-82d4-11ee-939d-92fbcf53809c.png

所以,如果你單獨使用 Feign 的時候,你就可以通過如上的兩種方式來設置超時時間。

SpringCloud 下 Feign 單獨使用超時時間設置

在 SpringCloud 環(huán)境下,只是對 Feign 進行了一層包裝,所以即使沒有 Ribbon 和注冊中心,Feign 也是可以單獨使用的,但是用法有點變化

注解都換成 SpringMVC 的注解

接口上需要加 @FeignClient 注解

用 @EnableFeignClients 掃描這些接口

不過,默認情況下 Feign 還是需要結合 Ribbon 來使用的。

如果你只想單獨使用 Feign,那么就設置一下 @FeignClient 注解的 url 屬性,指定請求的地址和端口就可以了。

8e78f232-82d4-11ee-939d-92fbcf53809c.png

所以,既然只是包裝,前面提到的兩種方式設置超時時間當然可以繼續(xù)使用:

通過 Feign.Builder

通過接口的方法參數

方法參數設置形式跟前面提到的一模一樣,但是通過 Feign.Builder 來設置卻不太一樣。

由于 SpringCloud 會自己創(chuàng)建 Feign.Builder,不需要我們創(chuàng)建,所以在設置 Options 時,Spring 提供了兩種快捷方式來設置。不過最終還是設置到 Feign.Builder 中。

1、聲明一個 Options Bean

Spring 在構建 Feign.Builder 的時,會從容器中查找 Options 這個 Bean,然后設置到 Feign.Builder 中。

@Configuration
publicclassFeignConfiguration{

@Bean
publicRequest.Optionsoptions(){
returnnewRequest.Options(8,TimeUnit.SECONDS,8,TimeUnit.SECONDS,true);
}

}

此時 debug 就可以看到設置到 Feign.Builder 的代碼。

8e98cd64-82d4-11ee-939d-92fbcf53809c.png

這段代碼在 FeignClientFactoryBean 中的 configureUsingConfiguration 方法中.

2、配置文件中設置

除了聲明 Bean 之外,Spring 還提供了通過配置文件的方式配置,如下:

同樣地,debug 就可以看見。

8ebb1dec-82d4-11ee-939d-92fbcf53809c.png

這段代碼在 FeignClientFactoryBean 中的 configureUsingConfiguration 方法中。

2、配置文件中設置

除了聲明 Bean 之外,Spring 還提供了通過配置文件的方式配置,如下:

feign:
client:
config:
default:
connectTimeout:10000
readTimeout:10000

同樣地,debug 就可以看見。

這段代碼在 FeignClientFactoryBean 中的 configureUsingConfiguration 方法中。

聲明 Bean 和配置文件都可以設置,那么同時設置哪種優(yōu)先級高呢?

如無特殊配置,遵守 SpringBoot 本身的配置規(guī)定

約定 > 配置 > 編碼

所以基于這個規(guī)定,配置文件的配置優(yōu)先級大于手動聲明 Bean 的優(yōu)先級。

到這,我們又學到了兩種 Spring 為了方便我們設置 Feign.Builder 提供的配置方式:

聲明 Options Bean

配置文件

把他們倆加到前面畫的圖中:

8ed7c9f6-82d4-11ee-939d-92fbcf53809c.png

所以,如果你使用了 SpringCloud 提供的方式來使用 Feign,那么就可以通過聲明 OptionsBean 和配置文件的方式更加方便地來設置超時時間。

最終其實還是通過 Feign.Builder 來設置的。

SpringCloud 下通過 Ribbon 來設置

當 Feign 配合 Ribbon 使用時,除了上面兩種方式之外,還可以通過 Ribbon 來設置超時時間。

但是這里我不知道你會不會好奇:

Ribbon 不是負載均衡組件,怎么可以設置超時時間?

跟 Ribbon 的定位有關,除了負載均衡組件之外,Ribbon 也干發(fā)送 HTTP 請求的事,也就是不配合 Feign,他照樣可以發(fā)送 HTTP 請求。

來個簡單 demo

8eee0c34-82d4-11ee-939d-92fbcf53809c.png

解釋一下上面的代碼意思:

第一步,設置 user 服務的兩個服務實例地址;

第二步,獲取 user 服務對應的 RestClient,這 RestClient 就可以用來發(fā)送 HTTP 請求;

第三步,構建一個 HTTP 請求;

第四步,就是發(fā)送 HTTP 請求,以負載均衡的方式。

這樣,此時就會從兩個服務實例中根據負載均衡選取一個服務地址發(fā)送 HTTP 請求,Ribbon 既然可以發(fā)送 HTTP 請求,那么自然而然就可以設置超時時間

Feign 在整合 Ribbon 的時候,為了統(tǒng)一配置,就默認將自己的超時時間交由 Ribbon 管理

所以,在默認情況下,Feign 的超時時間可以由 Ribbon 配置。而 Ribbon 默認連接和讀超時時間只有 1s,所以在默認情況下,Feign 的超時時間只有 1s。

8f10323c-82d4-11ee-939d-92fbcf53809c.png

IClientConfig 是 Ribbon 的配置類,Ribbon 所有的配置都可以從 IClientConfig 中獲取。

所以,在默認情況下,很容易就發(fā)生超時,不過我們可以通過配置文件修改即可。

ribbon:
ConnectTimeout:5000
ReadTimeout:5000

你知道你發(fā)現沒,上面說通過 Ribbon 設置 Feign 的超時時間,一直提到前面一直提到這個詞“默認”。

什么情況下叫默認呢?

所謂的默認,就是當你不主動設置 Feign 的超時時間的時候,就是默認。

換句話說,一旦你通過上面說的那些配置方式設置 Feign 的超時時間,就不是默認了

此時通過 Ribbon 設置的超時時間就不會生效了。

Feign 是如何在默認情況下將超時時間交給 Ribbon 管理的?

要想回答這個問題,就得先搬出前面反復提到的 Client 接口了。

在 SpringCloud 的環(huán)境下,有一個 Client 的實現,叫 LoadBalancerFeignClient。

8f1509ce-82d4-11ee-939d-92fbcf53809c.png

通過名字就可以看出,帶有負載均衡的 Client 實現,負載均衡的實現肯定是交給 Ribbon 來實現的。

所以,當 Feign 配合 Ribbon 時用的就是這個 Client 實現。

既然實現了 Client 接口,那就看看 execute 方法的實現邏輯。

8f24c01c-82d4-11ee-939d-92fbcf53809c.png

圖中 getClientConfig 方法就是判斷使用 Feign 或者 Ribbon 配置的核心邏輯。

核心的判斷邏輯就是這一行:

options==DEFAULT_OPTIONS

DEFAULT_OPTIONS 就是一個超時時間的常量。

8f56d156-82d4-11ee-939d-92fbcf53809c.png

當上述判斷條件成立時,就會通過 this.clientFactory.getClientConfig(clientName) (clientName) 獲取到 Ribbon 配置。

由于這是 Ribbon 的邏輯,這里就不深扒了,知道是這個意思就行。

當條件不成立時,用 Options 構建一個 FeignOptionsClientConfig。

8f7993e4-82d4-11ee-939d-92fbcf53809c.png

FeignOptionsClientConfig 就是簡單地將 Options 配置讀出來,設置到父類 DefaultClientConfigImpl 超時時間配置上。

DefaultClientConfigImpl 就算你不知道是什么也無所謂,你能看出的一件事就是,超時時間用的是傳遞給 Client 的 Options 參數。

所以,綜上,我們的問題就變得非常 easy 了,那就是什么時候。

options==DEFAULT_OPTIONS

只有當這個條件成立時,才使用 Ribbon 的配置。

這里我們先來捋一捋前面提到的東西。

前面我們反復提到,Client 的 Options 最終只來自于兩種配置:

Feign.Builder

方法參數

所以 DEFAULT_OPTIONS 這個 Options 一定是通過上面兩種方法中的其中一種設置的。

而方法參數是不可能設置的成 DEFAULT_OPTIONS。

因為這是我們控制的,只要我們參數不傳 DEFAULT_OPTIONS,那么永遠都不可能是 DEFAULT_OPTIONS。

此時只剩下一種情況,那就是 Spring 在構建在 Feign.Builder 的時候,設置成 DEFAULT_OPTIONS。

通過查找 DEFAULT_OPTIONS 的使用,我們可以追蹤到這么一段代碼。

8f84c480-82d4-11ee-939d-92fbcf53809c.png

這不就是前面提到的通過聲明 Bean 的方式來設置超時時間。

不同的是它加了 @ConditionalOnMissingBean,這個注解就是說,一旦我們自己沒有聲明 Options,就用他這個 Options。

到這終于真像大白了。

我們不設置超時時間,Spring 就會給 Feign.Builder 加一個 DEFAULT_OPTIONS 這個 Options。

在執(zhí)行的時候,發(fā)現是 DEFAULT_OPTIONS,說明我們沒有主動設置過超是時間,就會使用 Ribbon 的超時時間。

為了方便理清上面的邏輯,這里整一張圖。

8f94bcc8-82d4-11ee-939d-92fbcf53809c.png

雖然 Feign 可以使用 Ribbon 的超時時間,但是 Ribbon 的配置的優(yōu)先級是最最低的。

方法參數 > Feign 配置文件 > 聲明 Options > Ribbon 配置

Feign or Ribbon 配置用哪個好?

其實我個人更傾向于使用 Ribbon 的配置方式。

因為 Ribbon 除了可以設置超時時間之外,還可以配置重試機制、負載均衡等其它的配置

為了簡化和統(tǒng)一管理配置,使用 Ribbon 來配置超時時間。

可能你會有疑問,Feign 也支持重試機制,為什么不選擇 Feign?

這是因為 Feign 重試機制沒有 Ribbon 的好。

Ribbon 重試的時候會換一個服務實例來重試,因為原來出錯的可能不可用。

而 Feign 并不會換一個服務實例重試,他并不知道上一次使用的是哪個服務實例,這就導致可能會出現在一個不可用的服務實例上多次重試的情況。

引入 Hystrix 時超時時間設置

如果你之前的確沒有研究過關于 Feign 超時時間的配置關系,那么此時你應該有所收獲了。

但是這就結束了么?

不,事情沒那么簡單。

如果你的項目中使用了 Hystrix,那么就得小心前面說的那些配置了。

由于 Hystrix 跟 Feign 畢竟是一家人,所以當引入 Hystrix 時,Feign 就跟之前不一樣了。

Hystrix 會去干一件事,那就是給每個 Feign 的 HTTP 接口保護起來,畢竟 Hystrix 就是干保鏢這個事的。

但是這沒保護還好,一保護問題就不自覺地出現了。

Hystrix 在保護的時候,一旦發(fā)現被保護的接口執(zhí)行的時間超過 Hystrix 設置的最大時間,就直接進行降級操作。

怎么降級的,這里咱不關心,咱關心的是這個 Hystrix 超時的最大值是多少。

因為一旦這個時間小于 Feign 的超時時間,那么就會出現 Http 接口正在執(zhí)行,也沒有異常,僅僅是因為執(zhí)行時間長,就被降級了。

而 Hystrix 的默認的超時時間的最大值就只有 1s。

8fb15982-82d4-11ee-939d-92fbcf53809c.png

所以就算你 Feign 超時時間設置的再大,超過 1s 就算超時,然后被降級,太坑了……

所以我們需要修改這個默認的超時時間的最大值,具體的配置項如下:

hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds:30000

并且時間上大致要符合下面這個原則:

Hystrix 超時時間 >= (連接超時時間 + 讀超時時間) * 重試次數

重試次數我們前面也提到了,雖然一般我們不設置,但是為了嚴謹還是得加上,因為一次 Http 接口的執(zhí)行時間肯定跟重試次數有關,重試次數越多,時間就越長。

而連接超時時間 + 讀超時時間設置方式,前面提到很多次,不論是通過 Feign 本身設置還是通過 Ribbon 來設置,都是可以的。

總結

今天給大家扒了扒在不同使用條件下 Feign 的超時時間設置,總結起來大致如下:

單獨使用 Feign 時:通過 Feign.Builder 和方法參數;

SpringCloud 環(huán)境下單獨使用 Feign:方法參數、配置文件、聲明 Options Bean;

跟 Ribbon 配合使用:通過 Ribbon 的超時參數設置;

跟 Hystrix 配合使用:修改默認的超時時間,盡量符合 Hystrix 超時時間 >= (連接超時時間 + 讀超時時間) * 重試次數。







審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • URL
    URL
    +關注

    關注

    0

    文章

    139

    瀏覽量

    15297
  • 負載均衡
    +關注

    關注

    0

    文章

    104

    瀏覽量

    12350
  • HTTP接口
    +關注

    關注

    0

    文章

    21

    瀏覽量

    1768

原文標題:Feign 如何設置超時時間,不同情況下還真不一樣

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    ESP32-S2如何設置socket recv的超時時間?

    請教一下: 如何設置socket recv的超時時間? 參考的是TCP server例程,有其它的提供超時的例程嗎? 謝謝!
    發(fā)表于 06-21 07:27

    請問lwIP連接超時時間如何設置?

    目前的狀態(tài):使用connect連接到服務端,如果服務端掉線需要等待大概20s才會返回連接錯誤,時間有點長,沒看到connect有設置超時的功能,有沒有什么方法設置連接
    發(fā)表于 09-27 09:44

    數據庫打開達到超時時間沒有退出

    利用“DB Tools Open Connection VI”打開數據庫連接時,如果電腦有連接網線時出現上傳數據異常時,每次都要40秒左右才退出,而不是設置超時時間退出(超時設了3秒);如果沒有接網絡就正常;有哪位大神知道怎么
    發(fā)表于 07-02 21:27

    從機indication接收超時時間怎么更改?

    從機向主機indication消息后,主機會回復一個cfm事件,當主機沒有回復時,從機會超時一個超時事件,這個超時事件測試為30s,覺得有點長,當一直找不到哪里可以更改這個超時時間的,
    發(fā)表于 03-09 10:00

    如何去配置IWDG的超時時間

    IWDG是什么?IWDG有哪些功能?怎么去使用IWDG?如何去配置IWDG的超時時間
    發(fā)表于 08-02 10:04

    如何設置WWDG窗口看門狗的超時時間

    窗口看門狗WWDG的主要功能有哪些?如何設置WWDG窗口看門狗的超時時間
    發(fā)表于 09-24 07:33

    空閑中斷IDLE的超時時間如何設置

    空閑中斷IDLE的超時時間如何設置?
    發(fā)表于 12-10 06:23

    如何設置獨立看門狗的超時時間?有哪些注意事項?

    如何設置獨立看門狗的超時時間?有哪些注意事項?
    發(fā)表于 01-18 06:25

    請問一下TCP建立連接超時時間怎么修改?

    請問一下TCP建立連接超時時間怎么修改?
    發(fā)表于 08-25 14:44

    有什么辦法解決WWDG最大超時時間太短的問題?

    我想在 STM32F407 上使用其中一個看門狗,但令我驚訝的是 WWDG 的最大超時時間如此之短(大約 50 毫秒,具體取決于時鐘速度)。這是非常有限制的,例如扇區(qū)擦除花費的時間比這長得多(并且是
    發(fā)表于 12-23 06:19

    ESP32-S2如何設置socket recv的超時時間?

    請教一下:如何設置socket recv的超時時間?參考的是TCP server例程,有其它的提供超時的例程嗎?謝謝!
    發(fā)表于 02-20 07:30

    有沒有辦法設置在STA模式下獲取DHCP地址的超時時間?

    大家好。有沒有辦法設置在 STA 模式下獲取 DHCP 地址的超時時間?我想稍微增加它看起來 - 在慢速或低信號網絡環(huán)境中使用 4 秒。
    發(fā)表于 02-24 06:26

    怎么計算一個modbus的超時時間

    要怎么才能計算一個modbus的超時時間
    發(fā)表于 10-11 06:33

    時控開關怎么設置時時間?

    藍牙時控開關:通過藍牙與手機連接,15米范圍內使用手機對開關進行無線控制。不用接觸開關,就可以控制手動開、關,也可以設置時時間自動開、關。
    的頭像 發(fā)表于 11-10 10:34 ?8380次閱讀

    socket連接超時如何處理

    實現以及網絡環(huán)境。 1. 理解Socket連接超時 在TCP/IP協(xié)議中,socket連接超時通常指的是在建立連接、發(fā)送數據或接收數據的過程中,由于沒有在預期時間內完成操作,系統(tǒng)自動終止連接。這個預期
    的頭像 發(fā)表于 11-01 16:48 ?238次閱讀