Feign如何設(shè)置超時時間(不同情況)
今天來聊一聊前段時間看到的一個面試題,也是在實際項目中需要考慮的一個問題,F(xiàn)eign的超時時間如何設(shè)置?
Feign的超時時間設(shè)置方式并不固定,它取決于Feign在項目中是如何使用的,不同的使用方式,超時時間設(shè)置方式也不大相同,甚至還可能有坑。
前置知識
由于文章會涉及到Feign的底層知識,如果不懂點Feign的基本概念的話,后面就看不下去了
所以為了方便不了解Feign的小伙伴也能夠讀得懂文章,這里我就簡單地說說Feign的原理,點到為止,雖然不深入,但足夠應(yīng)付這篇文章了
Feign的作用
在項目中,我們經(jīng)常需要調(diào)用第三方提供的Http接口,此時我們就可以使用一些Http框架來實現(xiàn),比如HttpClient
public class HttpClientDemo { public static void main(String[] args) throws Exception { //創(chuàng)建一個HttpClient HttpClient httpClient = HttpClientBuilder.create().build(); //構(gòu)建一個get請求 HttpGet httpGet = new HttpGet("http://192.168.100.1:8080/order/1"); //發(fā)送請求,獲取響應(yīng) HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); //讀出響應(yīng)值 String response = EntityUtils.toString(httpEntity); System.out.println("Response: " + response); } }
如果項目中只有一兩個這種第三方接口這樣寫還行,但是一旦這種三方接口過多的話,每次都得這樣組裝參數(shù),發(fā)送請求,寫一堆同樣的代碼,就顯然很麻煩了。
所以為了簡化發(fā)送Http請求的開發(fā),減少重復(fù)代碼,F(xiàn)eign就出現(xiàn)了。
Feign是一個聲明式的Http框架
當(dāng)你需要調(diào)用Http接口時,你需要聲明一個接口,加一些注解就可以了
而像組裝參數(shù)、發(fā)送Http請求等重復(fù)性的工作都交給Feign來完成。
Feign的原理
雖然有了接口,但是僅僅有接口是不夠的,因為接口又不能創(chuàng)建對象,我們得需要對象。
Feign為了方便我們?yōu)榻涌趧?chuàng)建對象,提供的Feign.Builder
這個內(nèi)部類
這個類的作用就是解析接口的上的注解,為接口生成一個動態(tài)代理對象,后面通過這個代理對象就可以發(fā)送請求了。
這個內(nèi)部類有很多屬性,這些屬性都是Feign的核心組件。
在這些核心的組件中有一個叫Client
的,上圖中我圈出來了。
這個Client
類劃個重點,非常非常重要,本文討論的東西跟他有密切關(guān)系。
它只有一個方法Response execute(Request request, Options options)
方法的第一個參數(shù)Request
就是封裝了http請求的url、請求方法,請求頭、請求體之類的參數(shù)
第二個參數(shù)Options
就是本文的主題,封裝了超時時間。
返回值Response
就是封裝了一些響應(yīng)碼status、響應(yīng)頭之類的
所以通過方法的參數(shù)和返回值也可以猜出來,這個Client
作用是用來組裝Http請求參數(shù),發(fā)送Http請求的
并且http請求超時時間是根據(jù)傳給Client
的Options
參數(shù)來決定的
如果想更深一步了解Feign原理,可在公眾號菜單欄springcloud分類中查看
Feign單獨使用時超時時間設(shè)置
Feign本身就是一個http客戶端,可獨立使用,F(xiàn)eign提供了兩種超時時間設(shè)置方式
1、通過Feign.Builder設(shè)置
前面提到,Feign.Builder
的作用是為接口的動態(tài)代理對象的
Feign.Builder
里面有很多屬性,其中就有關(guān)于超時時間的屬性Options
如果你不設(shè)置,那么超時時間就是默認(rèn)的
默認(rèn)的就是連接超時10s,讀超時60s
所以可以通過設(shè)置Feign.Builder
中的options
來設(shè)置超時時間
來個demo
環(huán)境準(zhǔn)備,就是一個簡單的SpringBoot項目,引入一個Feign的依賴
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.5.RELEASE</version> </dependency> </dependencies>
聲明接口 + 注解
public interface UserApi { @RequestLine("GET /user/{userId}") User queryUser(@Param("userId") Integer userId); }
這里演示的是Feign原生的使用方式,脫離于SpringCloud環(huán)境,所以Spring的那些@GetMappring就不支持了,改用Feign本身提供的注解
測試代碼
public class FeignDemo { public static void main(String[] args) { UserApi client = Feign.builder() //設(shè)置連接和讀超時間都是5s .options(new Request.Options(5, TimeUnit.SECONDS, 5, TimeUnit.SECONDS, true)) .target(UserApi.class, "http://localhost:8088"); User user = client.queryUser(123); } }
這里面的請求路徑都是不存在的,因為我們只關(guān)心傳給Client
的Options
參數(shù)值
Client
在我們不設(shè)置的時候,就用默認(rèn)的實現(xiàn)Client.Default
斷點打到execute
方法的實現(xiàn),運行,走起
結(jié)果就是我們設(shè)置的5s
2、在接口方法參數(shù)設(shè)置
除了在通過Feign.Builder時設(shè)置之外,F(xiàn)eign還支持在接口的方法參數(shù)上設(shè)置
此時你只需要在接口的方法上加一個Options
類型的參數(shù)
@RequestLine("GET /user/{userId}") User queryUser(@Param("userId") Integer userId, Request.Options options);
這樣在傳參數(shù)時就可以設(shè)置超時時間了
User user = client.queryUser(123, new Request.Options(3, TimeUnit.SECONDS, 3, TimeUnit.SECONDS, true));
同樣地,debug就可以看見我們設(shè)置的3s了
這兩種設(shè)置超時時間的主要區(qū)別就是方法參數(shù)設(shè)置超時時間的優(yōu)先級高于Feign.Builder設(shè)置的超時時間
用一張圖來總結(jié)一下上面的關(guān)系
所以,如果你單獨使用Feign的時候,你就可以通過如上的兩種方式來設(shè)置超時時間。
SpringCloud下Feign單獨使用超時時間設(shè)置
在SpringCloud環(huán)境下,只是對Feign進(jìn)行了一層包裝,所以即使沒有Ribbon和注冊中心,F(xiàn)eign也是可以單獨使用的,但是用法有點變化
注解都換成SpringMVC的注解
接口上需要加@FeignClient注解
用@EnableFeignClients掃描這些接口
不過,默認(rèn)情況下Feign還是需要結(jié)合Ribbon來使用的
如果你只想單獨使用Feign,那么就設(shè)置一下@FeignClient注解的url屬性,指定請求的地址和端口就可以了
所以,既然只是包裝,前面提到的兩種方式設(shè)置超時時間當(dāng)然可以繼續(xù)使用:
通過Feign.Builder
通過接口的方法參數(shù)
方法參數(shù)設(shè)置形式跟前面提到的一模一樣,但是通過Feign.Builder來設(shè)置卻不太一樣
由于SpringCloud會自己創(chuàng)建Feign.Builder,不需要我們創(chuàng)建,所以在設(shè)置Options
時,Spring提供了兩種快捷方式來設(shè)置
不過最終還是設(shè)置到Feign.Builder中
1、聲明一個Options Bean
Spring在構(gòu)建Feign.Builder
的時,會從容器中查找Options
這個Bean,然后設(shè)置到Feign.Builder
中
@Configuration public class FeignConfiguration { @Bean public Request.Options options() { return new Request.Options(8, TimeUnit.SECONDS, 8, TimeUnit.SECONDS, true); } }
此時debug就可以看到設(shè)置到Feign.Builder
的代碼
這段代碼在FeignClientFactoryBean中的configureUsingConfiguration方法中
2、配置文件中設(shè)置
除了聲明Bean之外,Spring還提供了通過配置文件的方式配置,如下:
feign: client: config: default: connectTimeout: 10000 readTimeout: 10000
同樣地,debug就可以看見
這段代碼在FeignClientFactoryBean中的configureUsingConfiguration方法中
聲明Bean和配置文件都可以設(shè)置,那么同時設(shè)置哪種優(yōu)先級高呢?
如無特殊配置,遵守SpringBoot本身的配置規(guī)定
約定 > 配置 > 編碼
所以基于這個規(guī)定,配置文件的配置優(yōu)先級大于手動聲明Bean的優(yōu)先級。
到這,我們又學(xué)到了兩種Spring為了方便我們設(shè)置Feign.Builder
提供的配置方式:
聲明Options Bean
配置文件
把他們倆加到前面畫的圖中
所以,如果你使用了SpringCloud提供的方式來使用Feign,那么就可以通過聲明Options
Bean和配置文件的方式更加方便地來設(shè)置超時時間
最終其實還是通過Feign.Builder
來設(shè)置的
SpringCloud下通過Ribbon來設(shè)置
當(dāng)Feign配合Ribbon使用時,除了上面兩種方式之外,還可以通過Ribbon來設(shè)置超時時間。
但是這里我不知道你會不會好奇
Ribbon不是負(fù)載均衡組件,怎么可以設(shè)置超時時間?
其實這跟Ribbon的定位有關(guān),除了負(fù)載均衡組件之外,Ribbon也干發(fā)送Http請求的事,也就是不配合Feign,他照樣可以發(fā)送http請求。
來個簡單demo
解釋一下上面的代碼意思
第一步,設(shè)置user服務(wù)的兩個服務(wù)實例地址
第二步,獲取user服務(wù)對應(yīng)的RestClient,這RestClient就可以用來發(fā)送http請求
第三步,構(gòu)建一個http請求
第四步,就是發(fā)送http請求,以負(fù)載均衡的方式
這樣,此時就會從兩個服務(wù)實例中根據(jù)負(fù)載均衡選取一個服務(wù)地址發(fā)送http請求,
Ribbon既然可以發(fā)送Http請求,那么自然而然就可以設(shè)置超時時間
Feign在整合Ribbon的時候,為了統(tǒng)一配置,就默認(rèn)將自己的超時時間交由Ribbon管理
所以,在默認(rèn)情況下,F(xiàn)eign的超時時間可以由Ribbon配置
而Ribbon默認(rèn)連接和讀超時時間只有1s,所以在默認(rèn)情況下,F(xiàn)eign的超時時間只有1s。
IClientConfig是Ribbon的配置類,Ribbon所有的配置都可以從IClientConfig中獲取。
所以,在默認(rèn)情況下,很容易就發(fā)生超時,不過我們可以通過配置文件修改即可
ribbon: ConnectTimeout: 5000 ReadTimeout: 5000
你知道你發(fā)現(xiàn)沒,上面說通過Ribbon設(shè)置Feign的超時時間,一直提到前面一直提到這個詞
默認(rèn)
什么情況下叫默認(rèn)呢?
所謂的默認(rèn),就是當(dāng)你不主動設(shè)置Feign的超時時間的時候,就是默認(rèn)。
換句話說,一旦你通過上面說的那些配置方式設(shè)置Feign的超時時間,就不是默認(rèn)了
此時通過Ribbon設(shè)置的超時時間就不會生效了
Feign是如何在默認(rèn)情況下將超時時間交給Ribbon管理的?
要想回答這個問題,就得先搬出前面反復(fù)提到的Client接口了。
在SpringCloud的環(huán)境下,有一個Client的實現(xiàn),叫LoadBalancerFeignClient
通過名字就可以看出,帶有負(fù)載均衡的Client實現(xiàn),負(fù)載均衡的實現(xiàn)肯定是交給Ribbon來實現(xiàn)的
所以當(dāng)Feign配合Ribbon時用的就是這個Client實現(xiàn)
既然實現(xiàn)了Client
接口,那就看看execute
方法的實現(xiàn)邏輯
圖中getClientConfig
方法就是判斷使用Feign或者Ribbon配置的核心邏輯
核心的判斷邏輯就是這一行
options == DEFAULT_OPTIONS
DEFAULT_OPTIONS
就是一個超時時間的常量
當(dāng)上述判斷條件成立時,就會通過this.clientFactory.getClientConfig(clientName)
獲取到Ribbon配置
由于這是Ribbon的邏輯,這里就不深扒了,知道是這個意思就行
當(dāng)條件不成立時,用Options
構(gòu)建一個FeignOptionsClientConfig
FeignOptionsClientConfig
就是簡單地將Options
配置讀出來,設(shè)置到父類DefaultClientConfigImpl
超時時間配置上
DefaultClientConfigImpl
就算你不知道是什么也無所謂,你能看出的一件事就是,超時時間用的是傳遞給Client
的Options
參數(shù)
所以,綜上,我們的問題就變得非常easy了,那就是什么時候
options == DEFAULT_OPTIONS
只有當(dāng)這個條件成立時,才使用Ribbon的配置。
這里我們先來捋一捋前面提到的東西
前面我們反復(fù)提到,Client
的Options
最終只來自于兩種配置
Feign.Builder
方法參數(shù)
所以DEFAULT_OPTIONS
這個Options
一定是通過上面兩種方法中的其中一種設(shè)置的
而方法參數(shù)是不可能設(shè)置的成DEFAULT_OPTIONS
因為這是我們控制的,只要我們參數(shù)不傳DEFAULT_OPTIONS
,那么永遠(yuǎn)都不可能是DEFAULT_OPTIONS
。
此時只剩下一種情況,那就是Spring在構(gòu)建在Feign.Builder的時候,設(shè)置成DEFAULT_OPTIONS
。
通過查找DEFAULT_OPTIONS
的使用,我們可以追蹤到這么一段代碼
這不就是前面提到的通過聲明Bean的方式來設(shè)置超時時間
不同的是它加了@ConditionalOnMissingBean
,這個注解就是說,一旦我們自己沒有聲明Options
,就用他這個Options
到這終于真像大白了。
我們不設(shè)置超時時間,Spring就會給Feign.Builder加一個DEFAULT_OPTIONS
這個Options
在執(zhí)行的時候,發(fā)現(xiàn)是DEFAULT_OPTIONS
,說明我們沒有主動設(shè)置過超是時間,就會使用Ribbon的超時時間。
為了方便理清上面的邏輯,這里整一張圖
雖然Feign可以使用Ribbon的超時時間,但是Ribbon的配置的優(yōu)先級是最最低的
方法參數(shù) > Feign配置文件 > 聲明Options > Ribbon配置
Feign or Ribbon配置用哪個好?
其實我個人更傾向于使用Ribbon的配置方式。
因為Ribbon除了可以設(shè)置超時時間之外,還可以配置重試機(jī)制、負(fù)載均衡等其它的配置
為了簡化和統(tǒng)一管理配置,使用Ribbon來配置超時時間。
可能你會有疑問,F(xiàn)eign也支持重試機(jī)制,為什么不選擇Feign?
這是因為Feign重試機(jī)制沒有Ribbon的好
Ribbon重試的時候會換一個服務(wù)實例來重試,因為原來出錯的可能不可用
而Feign并不會換一個服務(wù)實例重試,他并不知道上一次使用的是哪個服務(wù)實例,這就導(dǎo)致可能會出現(xiàn)在一個不可用的服務(wù)實例上多次重試的情況。
引入Hystrix時超時時間設(shè)置
如果你之前的確沒有研究過關(guān)于Feign超時時間的配置關(guān)系,那么此時你應(yīng)該有所收獲了。
但是這就結(jié)束了么?
不,事情沒那么簡單。
如果你的項目中使用了Hystrix,那么就得小心前面說的那些配置了。
由于Hystrix跟Feign畢竟是一家人,所以當(dāng)引入Hystrix時,F(xiàn)eign就跟之前不一樣了。
Hystrix會去干一件事,那就是給每個Feign的http接口保護(hù)起來,畢竟Hystrix就是干保鏢這個事的。
但是這沒保護(hù)還好,一保護(hù)問題就不自覺地出現(xiàn)了。
Hystrix在保護(hù)的時候,一旦發(fā)現(xiàn)被保護(hù)的接口執(zhí)行的時間超過Hystrix設(shè)置的最大時間,就直接進(jìn)行降級操作。
怎么降級的,這里咱不關(guān)心,咱關(guān)心的是這個Hystrix超時的最大值是多少。
因為一旦這個時間小于Feign的超時時間,那么就會出現(xiàn)Http接口正在執(zhí)行,也沒有異常,僅僅是因為執(zhí)行時間長,就被降級了。
而Hystrix的默認(rèn)的超時時間的最大值就只有1s。
所以就算你Feign超時時間設(shè)置的再大,超過1s就算超時,然后被降級,太坑了。。
所以我們需要修改這個默認(rèn)的超時時間的最大值,具體的配置項如下
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 30000
并且時間上大致要符合下面這個原則
Hystrix超時時間 >= (連接超時時間 + 讀超時時間) * 重試次數(shù)
重試次數(shù)我們前面也提到了,雖然一般我們不設(shè)置,但是為了嚴(yán)謹(jǐn)還是得加上,因為一次Http接口的執(zhí)行時間肯定跟重試次數(shù)有關(guān),重試次數(shù)越多,時間就越長。
而連接超時時間 + 讀超時時間設(shè)置方式,前面提到很多次,不論是通過Feign本身設(shè)置還是通過Ribbon來設(shè)置,都是可以的
總結(jié)
今天給大家扒了扒在不同使用條件下Feign的超時時間設(shè)置,總結(jié)起來大致如下:
單獨使用Feign時:通過
Feign.Builder
和方法參數(shù)SpringCloud環(huán)境下單獨使用Feign:方法參數(shù)、配置文件、聲明
Options
Bean跟Ribbon配合使用:通過Ribbon的超時參數(shù)設(shè)置
跟Hystrix配合使用:修改默認(rèn)的超時時間,盡量符合 Hystrix超時時間 >= (連接超時時間 + 讀超時時間) * 重試次數(shù)
到此這篇關(guān)于Feign如何設(shè)置超時時間(不同情況)的文章就介紹到這了,更多相關(guān)Feign 超時時間內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決RedisTemplate調(diào)用increment報錯問題
這篇文章主要介紹了解決RedisTemplate調(diào)用increment報錯問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11聊聊spring boot的WebFluxTagsProvider的使用
這篇文章主要介紹了聊聊spring boot的WebFluxTagsProvider的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07SpringSecurity整合springBoot、redis實現(xiàn)登錄互踢功能
這篇文章主要介紹了SpringSecurity整合springBoot、redis實現(xiàn)登錄互踢,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05springboot接收json數(shù)據(jù)時,接收到空值問題
這篇文章主要介紹了springboot接收json數(shù)據(jù)時,接收到空值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05解決java.sql.SQLException:?validateConnection?false問題的方法匯總(最
這篇文章主要給大家介紹了關(guān)于解決java.sql.SQLException:?validateConnection?false問題的方法匯總,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03JAVA下單接口優(yōu)化實戰(zhàn)TPS性能提高10倍
今天小編就為大家分享一篇關(guān)于JAVA下單接口優(yōu)化實戰(zhàn)TPS性能提高10倍,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12