Spring cloud Feign 深度學(xué)習(xí)與應(yīng)用詳解
簡(jiǎn)介
Spring Cloud Feign是一個(gè)聲明式的Web Service客戶端,它的目的就是讓W(xué)eb Service調(diào)用更加簡(jiǎn)單。Feign提供了HTTP請(qǐng)求的模板,通過(guò)編寫簡(jiǎn)單的接口和插入注解,就可以定義好HTTP請(qǐng)求的參數(shù)、格式、地址等信息。Feign會(huì)完全代理HTTP請(qǐng)求,開(kāi)發(fā)時(shí)只需要像調(diào)用方法一樣調(diào)用它就可以完成服務(wù)請(qǐng)求及相關(guān)處理。開(kāi)源地址:https://github.com/OpenFeign/feign。Feign整合了Ribbon負(fù)載和Hystrix熔斷,可以不再需要顯式地使用這兩個(gè)組件??傮w來(lái)說(shuō),F(xiàn)eign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP編碼器和解碼器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的負(fù)載均衡;
- 支持HTTP請(qǐng)求和響應(yīng)的壓縮。
Spring Cloud Feign致力于處理客戶端與服務(wù)器之間的調(diào)用需求。隨著業(yè)務(wù)的擴(kuò)展和微服務(wù)數(shù)量的增多,不可避免的需要面對(duì)如下問(wèn)題:
- 彈性客戶端
- 雪崩效應(yīng)
簡(jiǎn)單來(lái)說(shuō),使用Spring Cloud Feign組件,他本身整合了Ribbon和Hystrix。可設(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ù)交互通過(guò)遠(yuǎn)程過(guò)程調(diào)用完成。這就帶來(lái)一個(gè)問(wèn)題,假設(shè)微服務(wù)A調(diào)用微服務(wù)B和微服務(wù)C,微服務(wù)B和微服務(wù)C又調(diào)用其它的微服務(wù),這就是所謂的“扇出”。如果扇出的鏈路上某個(gè)微服務(wù)的調(diào)用響應(yīng)時(shí)間過(guò)長(zhǎng)或者不可用,對(duì)微服務(wù)A的調(diào)用就會(huì)占用越來(lái)越多的系統(tǒng)資源,進(jìn)而引起系統(tǒng)崩潰,產(chǎn)生“雪崩效應(yīng)”。引發(fā)雪崩效應(yīng)的原因有:
- 硬件故障:如服務(wù)器宕機(jī),機(jī)房斷電,光纖被挖斷等;
- 流量激增:如異常流量,重試加大流量等;
- 緩存穿透:一般發(fā)生在應(yīng)用重啟,所有緩存失效時(shí),以及短時(shí)間內(nèi)大量緩存失效時(shí)。大量的緩存不命中,使請(qǐng)求直擊后端服務(wù),造成服務(wù)提供者超負(fù)荷運(yùn)行,引起服務(wù)不可用;
- 程序BUG:如程序邏輯導(dǎo)致內(nèi)存泄漏,JVM長(zhǎng)時(shí)間FullGC等;
- 同步等待:服務(wù)間采用同步調(diào)用模式,同步等待造成的資源耗盡;
- 服務(wù)降級(jí)故障:服務(wù)的降級(jí)可以是以間歇性的故障開(kāi)始,并形成不可逆轉(zhuǎn)的勢(shì)頭??赡荛_(kāi)始只是一小部分服務(wù)調(diào)用變慢,直到突然間應(yīng)用程序容器耗盡了線程(所有線程都在等待調(diào)用完成)并徹底崩潰。
彈性客戶端
客戶端彈性模式是在遠(yuǎn)程服務(wù)發(fā)生錯(cuò)誤或表現(xiàn)不佳時(shí)保護(hù)遠(yuǎn)程資源(另一個(gè)微服務(wù)調(diào)用或者數(shù)據(jù)庫(kù)查詢)免于崩潰。這些模式的目標(biāo)是為了能讓客戶端“快速失敗”,不消耗諸如數(shù)據(jù)庫(kù)連接、線程池之類的資源,還可以避免遠(yuǎn)程服務(wù)的問(wèn)題向客戶端的消費(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)重意思是請(qǐng)求時(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í)間太長(zhǎng),斷路器將介入并中斷調(diào)用。此外,如果對(duì)某個(gè)遠(yuǎn)程資源的調(diào)用失敗次數(shù)達(dá)到某個(gè)閾值,將會(huì)采取快速失敗策略,阻止將來(lái)調(diào)用失敗的遠(yuǎn)程資源。
后備(fallback)模式
當(dāng)遠(yuǎn)程調(diào)用失敗時(shí),將執(zhí)行替代代碼路徑,并嘗試通過(guò)其他方式來(lái)處理操作,而不是產(chǎn)生一個(gè)異常。也就是為遠(yuǎn)程操作提供一個(gè)應(yīng)急措施,而不是簡(jiǎn)單的拋出異常。
艙壁/隔板(bulkhead)模式
艙壁模式是建立在造船的基礎(chǔ)概念上。一艘船會(huì)被劃分為多個(gè)水密艙(艙壁),因而即使少數(shù)幾個(gè)部位被擊穿漏水,整艘船并不會(huì)被淹沒(méi)。將這個(gè)概念帶入到遠(yuǎn)程調(diào)用中,如果所有調(diào)用都使用的是同一個(gè)線程池來(lái)處理,那么很有可能一個(gè)緩慢的遠(yuǎn)程調(diào)用會(huì)拖垮整個(gè)應(yīng)用程序。在艙壁模式中可以隔離每個(gè)遠(yuǎn)程資源,并分配各自的線程池,使之互不影響。
Hystrix介紹
Hystrix,英文翻譯是豪豬,是一種保護(hù)機(jī)制,Netflix公司的一款組件。主頁(yè):https://github.com/Netflix/Hystrix/。Hystix是Netflix開(kāi)源的一個(gè)延遲和容錯(cuò)庫(kù),用于隔離訪問(wèn)遠(yuǎn)程服務(wù)、第三方庫(kù),防止出現(xiàn)級(jí)聯(lián)失敗。

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

2.Fallback-后備模式
Fallback相當(dāng)于是降級(jí)操作。對(duì)于查詢操作, 我們可以實(shí)現(xiàn)一個(gè)fallback方法, 當(dāng)請(qǐng)求后端服務(wù)出現(xiàn)異常的時(shí)候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設(shè)置的默認(rèn)值或者來(lái)自緩存。
3.資源隔離-艙壁(bulkhead)模式
在Hystrix中, 主要通過(guò)線程池來(lái)實(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)境被隔離開(kāi)了。這樣就算調(diào)用服務(wù)的代碼存在bug或者由于其他原因?qū)е伦约核诰€程池被耗盡時(shí), 不會(huì)對(duì)系統(tǒng)的其他服務(wù)造成影響。 但是帶來(lái)的代價(jià)就是維護(hù)多個(gè)線程池會(huì)對(duì)系統(tǒng)帶來(lái)額外的性能開(kāi)銷。如果是對(duì)性能有嚴(yán)格要求而且確信自己調(diào)用服務(wù)的客戶端代碼不會(huì)出問(wèn)題的話, 可以使用Hystrix的信號(hào)模式(Semaphores)來(lái)隔離資源。
Spring Cloud Feign 應(yīng)用
上面主要是講述了Feign模式管理客戶端方面應(yīng)對(duì)的一些問(wèn)題和理論知識(shí),下面將講述Feign結(jié)合Ribbon和Hystrix在項(xiàng)目中的落地應(yīng)用。
創(chuàng)建Feign
在Spring Boot項(xiàng)目中, 推薦在pom中添加Feign依賴(feign默認(rèn)會(huì)使用JDK自帶的 HttpUrlConnection ,相對(duì)于Apache的HttpComponent缺失連接池等擴(kuò)展信息,詳情見(jiàn):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èn)服務(wù)的方式,及通過(guò)服務(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即是對(duì)應(yīng)微服務(wù)的訪問(wèn)地址,path可設(shè)置或不設(shè)置,表示該服務(wù)下面的通用訪問(wèn)路徑。fallback是定義后備模式。
FeignClient模式通過(guò)Apache的HttpComponent封裝調(diào)用時(shí),需注意多參數(shù),json等數(shù)據(jù)的細(xì)節(jié)處理。
Feign Hystrix斷路器模式
Feign本身集成了Hystrix。但默認(rèn)情況下沒(méi)有啟動(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:
# 是否開(kāi)啟熔斷(默認(rèn)true)
enabled: true
# 熔斷生效至少請(qǐng)求數(shù)量(默認(rèn)20),當(dāng)同一HystrixCommand請(qǐng)求數(shù)量低于此值時(shí),熔斷不會(huì)開(kāi)啟
requestVolumeThreshold:20
# 失敗次數(shù)超過(guò)比例才開(kāi)啟熔斷
errorThresholdPercentage: 50
# 強(qiáng)制開(kāi)啟熔斷
#forceOpen: true
# 強(qiáng)制關(guān)閉熔斷
#forceClosed: true
execution:
isolation:
# THREAD:單獨(dú)開(kāi)啟線程執(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ā)起請(qǐng)求超過(guò)此值時(shí),請(qǐng)求會(huì)被拒絕,直接調(diào)用降級(jí)方法,異常信息關(guān)鍵字:could not acquire a semaphore for execution
maxConcurrentRequests: 1000
fallback:
isolation:
semaphore:
# 由于我們使用SEMAPHORE模式,當(dāng)每個(gè)feign并發(fā)發(fā)起請(qǐng)求調(diào)用降級(jí)方法超過(guò)此值,調(diào)用降級(jí)方法會(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")//這里的請(qǐng)求路徑需要和eureka-client中的請(qǐng)求路徑一致
public String test();//這里的方法名需要和eureka-client中的方法名一致
}
/**
* 服務(wù)發(fā)現(xiàn)-對(duì)應(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降級(jí)服務(wù)", cause);
return ResponseBody.fallback(cause, new Error("getItemDetail", "商品服務(wù)不可用"));
};
}
}
Feign Hystrix艙壁(bulkhead)模式
Feign集成了Hystrix,也可以設(shè)置客戶端為艙壁模式。通過(guò)設(shè)置Hystrix的配置文件即可。
Hystrix隔離級(jí)別由SEMAPHORE(信號(hào)量)模式切換為THREAD(線程池)模式,同時(shí)服務(wù)追蹤功能相應(yīng)調(diào)整適用THREAD模式。該模式有如下特性:
- 各上游服務(wù)(feign客戶端)線程資源隔離,相互不影響,可以實(shí)現(xiàn)完全的獨(dú)立配置。
- 由于feign請(qǐng)求是獨(dú)立線程,才可以真正意義上的實(shí)現(xiàn)超時(shí)降級(jí)功能(使用semaphore實(shí)際上是假的超時(shí)功能,比如超時(shí)設(shè)置1S,實(shí)際執(zhí)行3S,但整體還是會(huì)執(zhí)行3S,只是3S后會(huì)拋出TimeoutException觸發(fā)降級(jí)),而thread模式則能夠正在的在1S后直接Interrupt請(qǐng)求線程且立刻觸發(fā)降級(jí),達(dá)到真正的斷流保護(hù)作用。
- 開(kāi)啟線程池模式會(huì)額外開(kāi)銷服務(wù)器資源,在開(kāi)啟這種模式時(shí),線程池的數(shù)量,服務(wù)器資源還是需要監(jiān)控,綜合設(shè)置。
Hystrix艙壁(bulkhead)模式常用配置文件:
# 全局統(tǒng)一配置
hystrix:
command:
default:
execution:
isolation:
# 更改為THREAD,其余SEMAPHORE開(kāi)頭的配置可以去掉
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)超過(guò)coreSize的線程允許最大空閑時(shí)間
keepAliveTimeMinutes: 1
# 等待隊(duì)列,默認(rèn)-1即SynchronousQueue,直接交由線程池拒絕或者等待
maxQueueSize: -1
# 默認(rèn)5,這個(gè)值的出現(xiàn)是因?yàn)榫€程池的queueSize無(wú)法動(dòng)態(tài)變更,所以用這個(gè)值可以動(dòng)態(tài)變更來(lái)前置檢測(cè)是否拒絕,當(dāng)maxQueueSize為-1或者0時(shí),這個(gè)檢測(cè)直接通過(guò)后交由線程池自己處理,當(dāng)maxQueueSize大于0時(shí),由queueSize<queueSizeRejectionThreshold來(lái)決定是否拒絕請(qǐng)求,所以如果設(shè)置maxQueueSize,最終隊(duì)列拒絕效果是以此值為準(zhǔn)
queueSizeRejectionThreshold: 5
Feign Ribbon 負(fù)載均衡模式
Feign可通過(guò)配置參數(shù)設(shè)定Ribbon的運(yùn)行模式,Ribbon配置(所有配置參考com.netflix.client.config.CommonClientConfigKey和com.netflix.client.config.DefaultClientConfigImpl)。一般設(shè)置負(fù)載均衡的重試機(jī)制,服務(wù)輪詢模式,請(qǐng)求響應(yīng)時(shí)間等參數(shù)。

Feign模式下Ribbon常用配置參數(shù)如下:
ribbon: # 默認(rèn)相同的route不重試,可以避免一些各種重試引起的問(wèn)題,簡(jiǎn)單化(但服務(wù)提供方還是應(yīng)該盡量保證冪等性) MaxAutoRetries: 0 # 默認(rèn)只重試不同route一次 MaxAutoRetriesNextServer: 1 # 由于在前面feign文檔中已經(jīng)講到使用自己配置的HttpClient連接池,所以不需要配置ribbon連接池相關(guān)的任何屬性(因?yàn)榭紤]到每個(gè)服務(wù)提供方的不同,后期可能會(huì)更改回來(lái)使用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ù)載均衡策略,通過(guò)這里來(lái)設(shè)置 # ribbon子容器饑餓加載,避免偶爾因?yàn)榉?wù)重啟后第一次發(fā)起請(qǐng)求時(shí)延遲加載耗時(shí)造成fallback,但是會(huì)增加系統(tǒng)啟動(dòng)時(shí)間(新版才支持) eager-load: enabled: true clients: - a - b - c #一個(gè)客戶端遠(yuǎn)程多個(gè)微服務(wù),可針對(duì)單個(gè)微服務(wù)做特殊配置 # ribbon客戶端名稱(即feign客戶端名稱) <clientName>: ribbon: listOfServers: www.baidu.com xxx: xxx
Spring Cloud Feign 注意事項(xiàng)
fallback
Feign降級(jí)本地實(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降級(jí)工廠類,必須實(shí)現(xiàn)feign.hystrix.FallbackFactory接口,適用于復(fù)雜的根據(jù)異常類型動(dòng)態(tài)選擇降級(jí)實(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)掃描,注冊(cè)到通用配置中,會(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請(qǐng)求時(shí)長(zhǎng),最好小于hystrix時(shí)長(zhǎng)
*/
@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ù)訪問(wèn)路徑,最終訪問(wèn)路徑為:url+path( @FeignClient )+path( @RequestMapping )注意,無(wú)論使用自動(dòng)服務(wù)發(fā)現(xiàn)還是固定訪問(wèn)路徑方式, @FeignClient 注解的name或者value屬性不能為空(serviceId已經(jīng)摒棄)。
方法返回類型
通過(guò)Feign調(diào)用遠(yuǎn)程服務(wù),可以定義調(diào)用的方法返回void,業(yè)務(wù)對(duì)象類型或者 feign.Response 復(fù)雜類型。
method
必須使用 @RequestMapping 顯式聲明method,不能使用 @GetMapping 或者 @PostMapping 。
// 顯示指定方法 @RequestMapping(method = RequestMethod.POST)
consumes
凡是使用PHP服務(wù),因?yàn)檎?qǐng)求必須為json,必須添加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請(qǐng)求復(fù)雜對(duì)象
// 方式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,則可以直接用對(duì)象接受
@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è)置請(qǐng)求時(shí)長(zhǎng),Hystrix設(shè)置熔斷,Ribbon可以設(shè)置重試機(jī)制,請(qǐng)求時(shí)長(zhǎng)。這些參數(shù)在配置時(shí),要合理設(shè)置,避免沖突。為了確保Ribbon重試的時(shí)候不被熔斷,就需要讓Hystrix的超時(shí)時(shí)間大于Ribbon的超時(shí)時(shí)間,否則Hystrix命令超時(shí)后,該命令直接熔斷,重試機(jī)制就沒(méi)有任何意義了。
#ribbon超時(shí)配置為2000,請(qǐng)求超時(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請(qǐng)求,沒(méi)有連接池,但是對(duì)每個(gè)地址會(huì)保持一個(gè)長(zhǎng)連接,即利用HTTP的persistence connection 。建議采用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時(shí)時(shí)間等與性能息息相關(guān)的控制能力。Spring Cloud從 Brixtion.SR5 版本開(kāi)始支持這種替換,首先在項(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請(qǐng)求,建議自定義http請(qǐng)求的配置參數(shù)。
@Bean(destroyMethod = "close")
public CloseableHttpClient httpClient() {
// 最終存活時(shí)間還需要看服務(wù)端的keep-alive設(shè)置,和空閑時(shí)間以及間歇的validate是否通過(guò)
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過(guò)期主動(dòng)關(guān)閉連接造成失效,對(duì)于微服務(wù)場(chǎng)景可能還比較普遍,但受限HTTP設(shè)計(jì)理念,這也并發(fā)完全可靠,使用re-try/re-execute機(jī)制來(lái)彌補(bǔ),考慮到可能很多Niginx配置為5秒keep-alive
// TODO 這個(gè)值還待商榷
pool.setValidateAfterInactivity(5 * 1000);
return HttpClients.custom()
.setConnectionManager(pool)
// 連接空閑10s就回收,這個(gè)會(huì)啟動(dòng)獨(dú)立線程檢測(cè),所以必須聲明destroy方法來(lái)關(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ù)并沒(méi)有被服務(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();
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java微信公眾號(hào)開(kāi)發(fā)第一步 公眾號(hào)接入和access_token管理
這篇文章主要為大家介紹了java微信公眾號(hào)開(kāi)發(fā),主要內(nèi)容包括公眾號(hào)接入和access_token管理,感興趣的小伙伴們可以參考一下2016-01-01
關(guān)于Spring Boot對(duì)jdbc的支持問(wèn)題
這篇文章主要介紹了關(guān)于Spring Boot對(duì)jdbc的支持問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題
這篇文章主要介紹了idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
SpringBoot項(xiàng)目引入token設(shè)置方式
本文詳細(xì)介紹了JWT(JSON Web Token)的基本概念、結(jié)構(gòu)、應(yīng)用場(chǎng)景以及工作原理,通過(guò)動(dòng)手實(shí)踐,展示了如何在Spring Boot項(xiàng)目中實(shí)現(xiàn)JWT的生成、驗(yàn)證和攔截器配置2025-01-01
Java解決前端數(shù)據(jù)處理及亂碼問(wèn)題
大伙們有沒(méi)有遇到數(shù)據(jù)亂碼的問(wèn)題,真的是讓人心情煩躁,今天就來(lái)教下大家數(shù)據(jù)怎么傳輸?shù)角岸艘约皝y碼問(wèn)題怎么解決的,需要的朋友可以參考一下2021-12-12

