Spring cloud Feign 深度學(xué)習(xí)與應(yīng)用詳解
簡介
Spring Cloud Feign是一個(gè)聲明式的Web Service客戶端,它的目的就是讓W(xué)eb Service調(diào)用更加簡單。Feign提供了HTTP請求的模板,通過編寫簡單的接口和插入注解,就可以定義好HTTP請求的參數(shù)、格式、地址等信息。Feign會(huì)完全代理HTTP請求,開發(fā)時(shí)只需要像調(diào)用方法一樣調(diào)用它就可以完成服務(wù)請求及相關(guān)處理。開源地址:https://github.com/OpenFeign/feign。Feign整合了Ribbon負(fù)載和Hystrix熔斷,可以不再需要顯式地使用這兩個(gè)組件。總體來說,F(xiàn)eign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP編碼器和解碼器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的負(fù)載均衡;
- 支持HTTP請求和響應(yīng)的壓縮。
Spring Cloud Feign致力于處理客戶端與服務(wù)器之間的調(diào)用需求。隨著業(yè)務(wù)的擴(kuò)展和微服務(wù)數(shù)量的增多,不可避免的需要面對如下問題:
- 彈性客戶端
- 雪崩效應(yīng)
簡單來說,使用Spring Cloud Feign組件,他本身整合了Ribbon和Hystrix??稍O(shè)計(jì)一套穩(wěn)定可靠的彈性客戶端調(diào)用方案,避免整個(gè)系統(tǒng)出現(xiàn)雪崩效應(yīng)。
雪崩效應(yīng)
在微服務(wù)架構(gòu)中,微服務(wù)是完成一個(gè)單一的業(yè)務(wù)功能,這樣做的好處是可以做到解耦,每個(gè)微服務(wù)可以獨(dú)立演進(jìn)。但是,一個(gè)應(yīng)用可能會(huì)有多個(gè)微服務(wù)組成,微服務(wù)之間的數(shù)據(jù)交互通過遠(yuǎn)程過程調(diào)用完成。這就帶來一個(gè)問題,假設(shè)微服務(wù)A調(diào)用微服務(wù)B和微服務(wù)C,微服務(wù)B和微服務(wù)C又調(diào)用其它的微服務(wù),這就是所謂的“扇出”。如果扇出的鏈路上某個(gè)微服務(wù)的調(diào)用響應(yīng)時(shí)間過長或者不可用,對微服務(wù)A的調(diào)用就會(huì)占用越來越多的系統(tǒng)資源,進(jìn)而引起系統(tǒng)崩潰,產(chǎn)生“雪崩效應(yīng)”。引發(fā)雪崩效應(yīng)的原因有:
- 硬件故障:如服務(wù)器宕機(jī),機(jī)房斷電,光纖被挖斷等;
- 流量激增:如異常流量,重試加大流量等;
- 緩存穿透:一般發(fā)生在應(yīng)用重啟,所有緩存失效時(shí),以及短時(shí)間內(nèi)大量緩存失效時(shí)。大量的緩存不命中,使請求直擊后端服務(wù),造成服務(wù)提供者超負(fù)荷運(yùn)行,引起服務(wù)不可用;
- 程序BUG:如程序邏輯導(dǎo)致內(nèi)存泄漏,JVM長時(shí)間FullGC等;
- 同步等待:服務(wù)間采用同步調(diào)用模式,同步等待造成的資源耗盡;
- 服務(wù)降級故障:服務(wù)的降級可以是以間歇性的故障開始,并形成不可逆轉(zhuǎn)的勢頭。可能開始只是一小部分服務(wù)調(diào)用變慢,直到突然間應(yīng)用程序容器耗盡了線程(所有線程都在等待調(diào)用完成)并徹底崩潰。
彈性客戶端
客戶端彈性模式是在遠(yuǎn)程服務(wù)發(fā)生錯(cuò)誤或表現(xiàn)不佳時(shí)保護(hù)遠(yuǎn)程資源(另一個(gè)微服務(wù)調(diào)用或者數(shù)據(jù)庫查詢)免于崩潰。這些模式的目標(biāo)是為了能讓客戶端“快速失敗”,不消耗諸如數(shù)據(jù)庫連接、線程池之類的資源,還可以避免遠(yuǎn)程服務(wù)的問題向客戶端的消費(fèi)者進(jìn)行傳播,引發(fā)“雪崩”效應(yīng)。spring cloud Feign主要使用的有四種客戶端彈性模式:

客戶端負(fù)載均衡(client load balance)模式
Spring Cloud Feign集成Ribbon處理。Ribbon 是一個(gè)基于 http 和 tcp 客戶端的負(fù)載均衡,可以配置在客戶端,以輪詢、隨機(jī)、權(quán)重(權(quán)重意思是請求時(shí)間越久的server,其被分配給客戶端使用的可能性就越低。)等方式實(shí)現(xiàn)負(fù)載均衡。Feign其實(shí)不是做負(fù)載均衡的,負(fù)載均衡是Ribbon的功能,Feign只是集成了Ribbon 而已。Feign的作用的替代RestTemplate,性能比較低,但是可以使代碼可讀性很強(qiáng)。
斷路器(circuit breaker)模式
本模式模仿的是電路中的斷路器。有了軟件斷路器,當(dāng)遠(yuǎn)程服務(wù)被調(diào)用時(shí),斷路器將監(jiān)視這個(gè)調(diào)用,如果調(diào)用時(shí)間太長,斷路器將介入并中斷調(diào)用。此外,如果對某個(gè)遠(yuǎn)程資源的調(diào)用失敗次數(shù)達(dá)到某個(gè)閾值,將會(huì)采取快速失敗策略,阻止將來調(diào)用失敗的遠(yuǎn)程資源。
后備(fallback)模式
當(dāng)遠(yuǎn)程調(diào)用失敗時(shí),將執(zhí)行替代代碼路徑,并嘗試通過其他方式來處理操作,而不是產(chǎn)生一個(gè)異常。也就是為遠(yuǎn)程操作提供一個(gè)應(yīng)急措施,而不是簡單的拋出異常。
艙壁/隔板(bulkhead)模式
艙壁模式是建立在造船的基礎(chǔ)概念上。一艘船會(huì)被劃分為多個(gè)水密艙(艙壁),因而即使少數(shù)幾個(gè)部位被擊穿漏水,整艘船并不會(huì)被淹沒。將這個(gè)概念帶入到遠(yuǎn)程調(diào)用中,如果所有調(diào)用都使用的是同一個(gè)線程池來處理,那么很有可能一個(gè)緩慢的遠(yuǎn)程調(diào)用會(huì)拖垮整個(gè)應(yīng)用程序。在艙壁模式中可以隔離每個(gè)遠(yuǎn)程資源,并分配各自的線程池,使之互不影響。
Hystrix介紹
Hystrix,英文翻譯是豪豬,是一種保護(hù)機(jī)制,Netflix公司的一款組件。主頁:https://github.com/Netflix/Hystrix/。Hystix是Netflix開源的一個(gè)延遲和容錯(cuò)庫,用于隔離訪問遠(yuǎn)程服務(wù)、第三方庫,防止出現(xiàn)級聯(lián)失敗。

Hystrix特性
1.斷路器機(jī)制-斷路器模式
斷路器很好理解, 當(dāng)Hystrix Command請求后端服務(wù)失敗數(shù)量超過一定比例(默認(rèn)50%), 斷路器會(huì)切換到開路狀態(tài)(Open)。這時(shí)所有請求會(huì)直接失敗而不會(huì)發(fā)送到后端服務(wù)。斷路器保持在開路狀態(tài)一段時(shí)間后(默認(rèn)5秒), 自動(dòng)切換到半開路狀態(tài)(HALF-OPEN)。這時(shí)會(huì)判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(tài)(CLOSED), 否則重新切換到開路狀態(tài)(OPEN)。Hystrix的斷路器就像我們家庭電路中的保險(xiǎn)絲, 一旦后端服務(wù)不可用, 斷路器會(huì)直接切斷請求鏈, 避免發(fā)送大量無效請求影響系統(tǒng)吞吐量, 并且斷路器有自我檢測并恢復(fù)的能力。
熔斷器模式就像是那些容易導(dǎo)致錯(cuò)誤的操作的一種代理。這種代理能夠記錄最近調(diào)用發(fā)生錯(cuò)誤的次數(shù),然后決定使用允許操作繼續(xù),或者立即返回錯(cuò)誤。熔斷器就是保護(hù)服務(wù)高可用的最后一道防線。 熔斷器開關(guān)相互轉(zhuǎn)換的邏輯如下圖:

2.Fallback-后備模式
Fallback相當(dāng)于是降級操作。對于查詢操作, 我們可以實(shí)現(xiàn)一個(gè)fallback方法, 當(dāng)請求后端服務(wù)出現(xiàn)異常的時(shí)候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設(shè)置的默認(rèn)值或者來自緩存。
3.資源隔離-艙壁(bulkhead)模式
在Hystrix中, 主要通過線程池來實(shí)現(xiàn)資源隔離。通常在使用的時(shí)候應(yīng)該根據(jù)調(diào)用的遠(yuǎn)程服務(wù)劃分出多個(gè)線程池。例如調(diào)用產(chǎn)品服務(wù)的Command放入A線程池, 調(diào)用賬戶服務(wù)的Command放入B線程池. 這樣做的主要優(yōu)點(diǎn)是運(yùn)行環(huán)境被隔離開了。這樣就算調(diào)用服務(wù)的代碼存在bug或者由于其他原因?qū)е伦约核诰€程池被耗盡時(shí), 不會(huì)對系統(tǒng)的其他服務(wù)造成影響。 但是帶來的代價(jià)就是維護(hù)多個(gè)線程池會(huì)對系統(tǒng)帶來額外的性能開銷。如果是對性能有嚴(yán)格要求而且確信自己調(diào)用服務(wù)的客戶端代碼不會(huì)出問題的話, 可以使用Hystrix的信號(hào)模式(Semaphores)來隔離資源。
Spring Cloud Feign 應(yīng)用
上面主要是講述了Feign模式管理客戶端方面應(yīng)對的一些問題和理論知識(shí),下面將講述Feign結(jié)合Ribbon和Hystrix在項(xiàng)目中的落地應(yīng)用。
創(chuàng)建Feign
在Spring Boot項(xiàng)目中, 推薦在pom中添加Feign依賴(feign默認(rèn)會(huì)使用JDK自帶的 HttpUrlConnection ,相對于Apache的HttpComponent缺失連接池等擴(kuò)展信息,詳情見:FeignRibbonClientAutoConfiguration)。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
在App啟動(dòng)類中,設(shè)置啟用Feign:
@EnableFeignClients
public class App {
}
搭建一個(gè)Feign Client基本配置:
@FeignClient(value="wl-service")
public interface WlFeignClient {
@RequestMapping(method = RequestMethod.GET, value= "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.POST, value= "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
@FeignClient
在此處可以配置客戶端訪問服務(wù)的方式,及通過服務(wù)名走服務(wù)發(fā)現(xiàn)模式和http地址模式,其參數(shù)可配置如:
- 服務(wù)發(fā)現(xiàn):@FeignClient(value = "wl-v1-00", fallback = ArticleSystemRemoteFallback.class)
- http地址:@FeignClient(name = "wl-test", url = "${test.url}", path = "combwl", fallbackFactory = GoodsGroupFeignFallbackFactory.class)
- 服務(wù)發(fā)現(xiàn) 模式value及微服務(wù)的名稱,fallback即定義后備模式,當(dāng)觸發(fā)熔斷時(shí),定義后備返回接口,便于客戶端“快速失敗”。
- http地址模式 url即是對應(yīng)微服務(wù)的訪問地址,path可設(shè)置或不設(shè)置,表示該服務(wù)下面的通用訪問路徑。fallback是定義后備模式。
FeignClient模式通過Apache的HttpComponent封裝調(diào)用時(shí),需注意多參數(shù),json等數(shù)據(jù)的細(xì)節(jié)處理。
Feign Hystrix斷路器模式
Feign本身集成了Hystrix。但默認(rèn)情況下沒有啟動(dòng)。必須顯示聲明(feign.hystrix.enabled=true)。客戶端應(yīng)用就是在配置文件中設(shè)置hystrix的配置,項(xiàng)目自動(dòng)根據(jù)配置文件參數(shù)調(diào)整。hystrix配置(所有的配置可以參考com.netflix.hystrix.HystrixCommandProperties這個(gè)類)。

常用的hystrix配置設(shè)置如下:
hystrix:
command:
default:
circuitBreaker:
# 是否開啟熔斷(默認(rèn)true)
enabled: true
# 熔斷生效至少請求數(shù)量(默認(rèn)20),當(dāng)同一HystrixCommand請求數(shù)量低于此值時(shí),熔斷不會(huì)開啟
requestVolumeThreshold:20
# 失敗次數(shù)超過比例才開啟熔斷
errorThresholdPercentage: 50
# 強(qiáng)制開啟熔斷
#forceOpen: true
# 強(qiáng)制關(guān)閉熔斷
#forceClosed: true
execution:
isolation:
# THREAD:單獨(dú)開啟線程執(zhí)行;SEMAPHORE:在調(diào)用線程上執(zhí)行(由于我們現(xiàn)有框架中FeignUserContextInterceptor中使用了ThreadLocal,所以必須使用第二種方式)
18. strategy: SEMAPHORE
19. thread:
20. # 執(zhí)行超時(shí)時(shí)間(這個(gè)時(shí)間設(shè)置很重要,因?yàn)镠ystrixCommand會(huì)包裝RibbonClient實(shí)例,那么這個(gè)時(shí)間就必須要大于ribbion timeout * retry,后面Ribbon章節(jié)會(huì)介紹)
timeoutInMilliseconds: 2000
semaphore:
# 由于我們使用SEMAPHORE模式,當(dāng)每個(gè)feign并發(fā)發(fā)起請求超過此值時(shí),請求會(huì)被拒絕,直接調(diào)用降級方法,異常信息關(guān)鍵字:could not acquire a semaphore for execution
maxConcurrentRequests: 1000
fallback:
isolation:
semaphore:
# 由于我們使用SEMAPHORE模式,當(dāng)每個(gè)feign并發(fā)發(fā)起請求調(diào)用降級方法超過此值,調(diào)用降級方法會(huì)被拒絕,直接拋出異常,異常信息關(guān)鍵字:fallback execution rejected
maxConcurrentRequests: 1000
Feign Hystrix后備模式
后備模式就是在遠(yuǎn)程調(diào)用服務(wù)時(shí),被斷路器切斷或服務(wù)調(diào)用超時(shí)時(shí),返回的一種備用方案。應(yīng)用舉例如下:
直接設(shè)置fallback,該模式不便于調(diào)試具體遠(yuǎn)程服務(wù)調(diào)用出錯(cuò)的信息。
/**
* 服務(wù)發(fā)現(xiàn)模式
*/
@FeignClient(name = "eureka-client",fallback = OpenFeignFallbackServiceImpl.class)//eureka-client工程的服務(wù)名稱
public interface OpenFeignService {
@GetMapping("/name")//這里的請求路徑需要和eureka-client中的請求路徑一致
public String test();//這里的方法名需要和eureka-client中的方法名一致
}
/**
* 服務(wù)發(fā)現(xiàn)-對應(yīng)后備模式的方法定義
*/
@Service
public class OpenFeignFallbackServiceImpl implements OpenFeignService{
@Override
public String test() {
return "調(diào)用服務(wù)失?。?;
}
除了fallback模式,還可以調(diào)用fallbackFactory,這種可以記錄遠(yuǎn)程調(diào)用失敗的具體明細(xì)異常。建議采用此方案設(shè)置后備模式。
/**
* 聲明調(diào)用客戶端
*/
@FeignClient(name = "wl-sku", url = "${wl.url}", path = "wl", fallbackFactory = WlSkuFeignFallbackFactory.class)
public interface WlSkuFeign {
/**
* 基于商品編碼獲取商品銷售屬性明細(xì)
*
* @param relationId 參數(shù)編碼
* @return
*/
@RequestMapping(method = RequestMethod.GET, path = "/item/{relation_id}")
ResponseBody<GoodsItemDto, EmptyMeta> getItemDetail(@PathVariable("relation_id") Integer relationId);
}
/**
* 申明后備模式
*
*/
@Component
public class WlSkuFeignFallbackFactory implements FallbackFactory<WlSkuFeign> {
@Override
public WlSkuFeign create(Throwable cause) {
return relationId -> {
ErrorLogger.getInstance().log("商品sku getItemDetail降級服務(wù)", cause);
return ResponseBody.fallback(cause, new Error("getItemDetail", "商品服務(wù)不可用"));
};
}
}
Feign Hystrix艙壁(bulkhead)模式
Feign集成了Hystrix,也可以設(shè)置客戶端為艙壁模式。通過設(shè)置Hystrix的配置文件即可。
Hystrix隔離級別由SEMAPHORE(信號(hào)量)模式切換為THREAD(線程池)模式,同時(shí)服務(wù)追蹤功能相應(yīng)調(diào)整適用THREAD模式。該模式有如下特性:
- 各上游服務(wù)(feign客戶端)線程資源隔離,相互不影響,可以實(shí)現(xiàn)完全的獨(dú)立配置。
- 由于feign請求是獨(dú)立線程,才可以真正意義上的實(shí)現(xiàn)超時(shí)降級功能(使用semaphore實(shí)際上是假的超時(shí)功能,比如超時(shí)設(shè)置1S,實(shí)際執(zhí)行3S,但整體還是會(huì)執(zhí)行3S,只是3S后會(huì)拋出TimeoutException觸發(fā)降級),而thread模式則能夠正在的在1S后直接Interrupt請求線程且立刻觸發(fā)降級,達(dá)到真正的斷流保護(hù)作用。
- 開啟線程池模式會(huì)額外開銷服務(wù)器資源,在開啟這種模式時(shí),線程池的數(shù)量,服務(wù)器資源還是需要監(jiān)控,綜合設(shè)置。
Hystrix艙壁(bulkhead)模式常用配置文件:
# 全局統(tǒng)一配置
hystrix:
command:
default:
execution:
isolation:
# 更改為THREAD,其余SEMAPHORE開頭的配置可以去掉
strategy: THREAD
thread:
# 默認(rèn)1000
timeoutInMilliseconds: 2000
threadpool:
default:
# 這個(gè)屬性很重要,默認(rèn)false。當(dāng)false時(shí):maximumSize=coreSize,當(dāng)true時(shí):取值Math.max(maximumSize,coreSize),所以如果想設(shè)置最大數(shù),必須設(shè)置為true
allowMaximumSizeToDivergeFromCoreSize: false
# 默認(rèn)10
coreSize: 10
maximumSize: 10
# 默認(rèn)1M,線程池內(nèi)超過coreSize的線程允許最大空閑時(shí)間
keepAliveTimeMinutes: 1
# 等待隊(duì)列,默認(rèn)-1即SynchronousQueue,直接交由線程池拒絕或者等待
maxQueueSize: -1
# 默認(rèn)5,這個(gè)值的出現(xiàn)是因?yàn)榫€程池的queueSize無法動(dòng)態(tài)變更,所以用這個(gè)值可以動(dòng)態(tài)變更來前置檢測是否拒絕,當(dāng)maxQueueSize為-1或者0時(shí),這個(gè)檢測直接通過后交由線程池自己處理,當(dāng)maxQueueSize大于0時(shí),由queueSize<queueSizeRejectionThreshold來決定是否拒絕請求,所以如果設(shè)置maxQueueSize,最終隊(duì)列拒絕效果是以此值為準(zhǔn)
queueSizeRejectionThreshold: 5
Feign Ribbon 負(fù)載均衡模式
Feign可通過配置參數(shù)設(shè)定Ribbon的運(yùn)行模式,Ribbon配置(所有配置參考com.netflix.client.config.CommonClientConfigKey和com.netflix.client.config.DefaultClientConfigImpl)。一般設(shè)置負(fù)載均衡的重試機(jī)制,服務(wù)輪詢模式,請求響應(yīng)時(shí)間等參數(shù)。

Feign模式下Ribbon常用配置參數(shù)如下:
ribbon: # 默認(rèn)相同的route不重試,可以避免一些各種重試引起的問題,簡單化(但服務(wù)提供方還是應(yīng)該盡量保證冪等性) MaxAutoRetries: 0 # 默認(rèn)只重試不同route一次 MaxAutoRetriesNextServer: 1 # 由于在前面feign文檔中已經(jīng)講到使用自己配置的HttpClient連接池,所以不需要配置ribbon連接池相關(guān)的任何屬性(因?yàn)榭紤]到每個(gè)服務(wù)提供方的不同,后期可能會(huì)更改回來使用ribbon連接池方式) # 默認(rèn)5000 ReadTimeout: 5000 # 默認(rèn)2000 ConnectTimeout: 2000 # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置規(guī)則 隨機(jī) # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置規(guī)則 輪詢 # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置規(guī)則 重試 # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置規(guī)則 響應(yīng)時(shí)間權(quán)重 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置規(guī)則 最空閑連接策 # 后續(xù)可能會(huì)自定義一些負(fù)載均衡策略,通過這里來設(shè)置 # ribbon子容器饑餓加載,避免偶爾因?yàn)榉?wù)重啟后第一次發(fā)起請求時(shí)延遲加載耗時(shí)造成fallback,但是會(huì)增加系統(tǒng)啟動(dòng)時(shí)間(新版才支持) eager-load: enabled: true clients: - a - b - c #一個(gè)客戶端遠(yuǎn)程多個(gè)微服務(wù),可針對單個(gè)微服務(wù)做特殊配置 # ribbon客戶端名稱(即feign客戶端名稱) <clientName>: ribbon: listOfServers: www.baidu.com xxx: xxx
Spring Cloud Feign 注意事項(xiàng)
fallback
Feign降級本地實(shí)現(xiàn),必須實(shí)現(xiàn)當(dāng)前Feign接口,且必須聲明為一個(gè)bean,feign調(diào)用異常時(shí)會(huì)自動(dòng)調(diào)用實(shí)現(xiàn)方法。
@Component
public class WlFeignFallback implements WlFeign {
@Override
public ResponseBody<List<Object>, EmptyMeta> getTest() {
return ResponseBody.fail(new Error("xx", "xx"));
}
}
fallbackFactory
Feign降級工廠類,必須實(shí)現(xiàn)feign.hystrix.FallbackFactory接口,適用于復(fù)雜的根據(jù)異常類型動(dòng)態(tài)選擇降級實(shí)現(xiàn)類(也必須實(shí)現(xiàn)當(dāng)前Feign接口),并且這個(gè)工廠類也必須聲明為一個(gè)bean。(可以獲取詳細(xì)異常信息,首選)。
configuration
自定義的獨(dú)立Feign客戶端的配置類,可以覆蓋Feign默認(rèn)的任何通用的Logger.Level,Retryer,Request.Options,RequestInterceptor,SetterFactory。特別注意,自定義的Configuration類不能加 @Configuration 注解,否則會(huì)被自動(dòng)掃描,注冊到通用配置中,會(huì)被全局Feign使用,同時(shí)方法必須加 @Bean 注解。
/**
* feign全局配置
*
* @Configuration 加上為全局,不加為自定義
*/
@Configuration
public class FeignConfiguration {
/**
* feign日志
*/
@Profile({"self", "local", "dev"})
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
/**
* http請求時(shí)長,最好小于hystrix時(shí)長
*/
@Bean
public Request.Options options() {
return new Request.Options(2000, 3500);
}
/**
* 使用默認(rèn)的不重試機(jī)制,單獨(dú)feign有特殊需求單獨(dú)配置
*/
public Retryer retryer() {
// 最小重試間隔,最大重試間隔,最多嘗試次數(shù)(包括第一次)
return new Retryer.Default(100L, 500L, 2);
}
url/path
顯示聲明固定服務(wù)訪問路徑,最終訪問路徑為:url+path( @FeignClient )+path( @RequestMapping )注意,無論使用自動(dòng)服務(wù)發(fā)現(xiàn)還是固定訪問路徑方式, @FeignClient 注解的name或者value屬性不能為空(serviceId已經(jīng)摒棄)。
方法返回類型
通過Feign調(diào)用遠(yuǎn)程服務(wù),可以定義調(diào)用的方法返回void,業(yè)務(wù)對象類型或者 feign.Response 復(fù)雜類型。
method
必須使用 @RequestMapping 顯式聲明method,不能使用 @GetMapping 或者 @PostMapping 。
// 顯示指定方法 @RequestMapping(method = RequestMethod.POST)
consumes
凡是使用PHP服務(wù),因?yàn)檎埱蟊仨殲閖son,必須添加consumes=MediaType.APPLICATION_JSON_VALUE(不能使用MediaType.APPLICATION_JSON_UTF8_VALUE,因?yàn)閍pache http ContentType在校驗(yàn)時(shí)不允許有'“‘,',‘,';‘出現(xiàn),詳情參考: org.apache.http.entity.ContentType valid(String s) 方法)。
GET請求復(fù)雜對象
// 方式1:使用Map傳輸
@RequestMapping(path="xxx", method=GET)
ResponseBody<T> test(@RequestParam Map<String, Object> map) {
}
// 方式2:獨(dú)立設(shè)置param
@RequestMapping(path="xxx", method=GET)
ResponseBody<T> test(@RequestParam("aaa") String aa, @RequestParam("bb") int bb) {
}
支持application/x-www-form-urlencoded格式http接口
// 如果接口返回類型是text/html,必須用string接受,然后手動(dòng)反序列化,如果是applicatin/json,則可以直接用對象接受
@RequestMapping(path="xxx", method=POST, consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String test(@RequestBody MultiValueMap<String, String> map) {
}
@RequestMapping(path="xxx", method=POST, consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public RequestBody test(String content) {
}
Feign,Hystrix,Ribbon配置參數(shù)注意
Feign本身可以設(shè)置重試,還可以設(shè)置請求時(shí)長,Hystrix設(shè)置熔斷,Ribbon可以設(shè)置重試機(jī)制,請求時(shí)長。這些參數(shù)在配置時(shí),要合理設(shè)置,避免沖突。為了確保Ribbon重試的時(shí)候不被熔斷,就需要讓Hystrix的超時(shí)時(shí)間大于Ribbon的超時(shí)時(shí)間,否則Hystrix命令超時(shí)后,該命令直接熔斷,重試機(jī)制就沒有任何意義了。
#ribbon超時(shí)配置為2000,請求超時(shí)后,該實(shí)例會(huì)重試1次,更新實(shí)例會(huì)重試1次。 service-hi: ribbon: ReadTimeout: 2000 ConnectTimeout: 1000 MaxAutoRetries: 1 MaxAutoRetriesNextServer: 1 hystrix: command: default: execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 8000
Feign的HTTP Client
Feign在默認(rèn)情況下使用的是JDK原生的URLConnection發(fā)送HTTP請求,沒有連接池,但是對每個(gè)地址會(huì)保持一個(gè)長連接,即利用HTTP的persistence connection 。建議采用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時(shí)時(shí)間等與性能息息相關(guān)的控制能力。Spring Cloud從 Brixtion.SR5 版本開始支持這種替換,首先在項(xiàng)目中聲明Apache HTTP Client和 feign-httpclient 依賴。
<!-- 使用Apache HttpClient替換Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
為了合理的利用Apache HTTP Client做http請求,建議自定義http請求的配置參數(shù)。
@Bean(destroyMethod = "close")
public CloseableHttpClient httpClient() {
// 最終存活時(shí)間還需要看服務(wù)端的keep-alive設(shè)置,和空閑時(shí)間以及間歇的validate是否通過
PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
pool.setMaxTotal(2000);
// 目前只有一個(gè)路由,默認(rèn)等于最大值,根據(jù)業(yè)務(wù)并發(fā)量設(shè)置
pool.setDefaultMaxPerRoute(2000);
// 檢查非活動(dòng)連接,避免服務(wù)端重啟后或者服務(wù)端keep-alive過期主動(dòng)關(guān)閉連接造成失效,對于微服務(wù)場景可能還比較普遍,但受限HTTP設(shè)計(jì)理念,這也并發(fā)完全可靠,使用re-try/re-execute機(jī)制來彌補(bǔ),考慮到可能很多Niginx配置為5秒keep-alive
// TODO 這個(gè)值還待商榷
pool.setValidateAfterInactivity(5 * 1000);
return HttpClients.custom()
.setConnectionManager(pool)
// 連接空閑10s就回收,這個(gè)會(huì)啟動(dòng)獨(dú)立線程檢測,所以必須聲明destroy方法來關(guān)閉獨(dú)立線程
.evictIdleConnections(10, TimeUnit.SECONDS)
// 建立連接時(shí)間和從連接池獲取連接時(shí)間,以及數(shù)據(jù)傳輸時(shí)間
.setDefaultRequestConfig(RequestConfig.custom()
// http建立連接超時(shí)時(shí)間
.setConnectTimeout(1000)
// 從連接池獲取連接超時(shí)時(shí)間
.setConnectionRequestTimeout(3000)
// socket超時(shí)時(shí)間
.setSocketTimeout(10000)
.build())
// 自定義重試機(jī)制
.setRetryHandler((exception, executionCount, context) -> {
// 目前只允許重試一次
if (executionCount > 1) {
return false;
}
// 如果是服務(wù)端主動(dòng)關(guān)閉連接的,數(shù)據(jù)并沒有被服務(wù)端接受,可以重試
if (exception instanceof NoHttpResponseException) {
return true;
}
// 不要重試SSL握手異常
if (exception instanceof SSLHandshakeException) {
return false;
}
// 超時(shí)
if (exception instanceof InterruptedIOException) {
return false;
}
// 目標(biāo)服務(wù)器不可達(dá)
if (exception instanceof UnknownHostException) {
return false;
}
// SSL握手異常
if (exception instanceof SSLException) {
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
String get = "GET";
// GET方法是冪等的,可以重試
if (request.getRequestLine().getMethod().equalsIgnoreCase(get)) {
return true;
}
return false;
})
// 默認(rèn)的ConnectionKeepAliveStrategy就是動(dòng)態(tài)根據(jù)keep-alive計(jì)算的
.build();
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot使用MyBatis-Flex實(shí)現(xiàn)靈活的數(shù)據(jù)庫訪問
MyBatisFlex是一款優(yōu)秀的持久層框架,本文主要介紹了SpringBoot使用MyBatis-Flex實(shí)現(xiàn)靈活的數(shù)據(jù)庫訪問,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
springmvc使用JSR-303進(jìn)行數(shù)據(jù)校驗(yàn)實(shí)例
本篇文章主要介紹了詳解springmvc使用JSR-303進(jìn)行數(shù)據(jù)校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
SpringBoot集成Swagger構(gòu)建api文檔的操作
這篇文章主要介紹了SpringBoot集成Swagger構(gòu)建api文檔的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Jmeter關(guān)聯(lián)實(shí)現(xiàn)及參數(shù)化使用解析
這篇文章主要介紹了Jmeter關(guān)聯(lián)實(shí)現(xiàn)及參數(shù)化使用解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
基于JTable的列寬與內(nèi)容自適應(yīng)的實(shí)現(xiàn)方法
本篇文章是對JTable的列寬與內(nèi)容自適應(yīng)的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
SpringData JPA Mongodb查詢部分字段問題
這篇文章主要介紹了SpringData JPA Mongodb查詢部分字段問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

