javascript
Spring Cloud Feign设计原理
點擊關注,快速進階高級架構(gòu)師
作者:亦山札記
什么是Feign?
Feign 的英文表意為“假裝,偽裝,變形”, 是一個http請求調(diào)用的輕量級框架,可以以Java接口注解的方式調(diào)用Http請求,而不用像Java中通過封裝HTTP請求報文的方式直接調(diào)用。Feign通過處理注解,將請求模板化,當實際調(diào)用的時候,傳入?yún)?shù),根據(jù)參數(shù)再應用到請求上,進而轉(zhuǎn)化成真正的請求,這種請求相對而言比較直觀。
Feign被廣泛應用在Spring Cloud 的解決方案中,是學習基于Spring Cloud 微服務架構(gòu)不可或缺的重要組件。
開源項目地址:
https://github.com/OpenFeign/feign
Feign解決了什么問題?
封裝了Http調(diào)用流程,更適合面向接口化的變成習慣
在服務調(diào)用的場景中,我們經(jīng)常調(diào)用基于Http協(xié)議的服務,而我們經(jīng)常使用到的框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty等等,這些框架在基于自身的專注點提供了自身特性。而從角色劃分上來看,他們的職能是一致的提供Http調(diào)用服務。具體流程如下:
Feign是如何設計的?
PHASE 1. 基于面向接口的動態(tài)代理方式生成實現(xiàn)類
在使用feign 時,會定義對應的接口類,在接口類上使用Http相關的注解,標識HTTP請求參數(shù)信息,如下所示:
在Feign 底層,通過基于面向接口的動態(tài)代理方式生成實現(xiàn)類,將請求調(diào)用委托到動態(tài)代理實現(xiàn)類,基本原理如下所示:
PHASE 2. 根據(jù)Contract協(xié)議規(guī)則,解析接口類的注解信息,解析成內(nèi)部表現(xiàn):
Feign 定義了轉(zhuǎn)換協(xié)議,定義如下:
默認Contract 實現(xiàn)
Feign 默認有一套自己的協(xié)議規(guī)范,規(guī)定了一些注解,可以映射成對應的Http請求,如官方的一個例子:
上述的例子中,嘗試調(diào)用GitHub.getContributors("foo","myrepo")的的時候,會轉(zhuǎn)換成如下的HTTP請求:
GET /repos/foo/myrepo/contributorsHOST XXXX.XXX.XXX
Feign 默認的協(xié)議規(guī)范
具體FeignContract 是如何解析的,不在本文的介紹范圍內(nèi),詳情請參考代碼:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Contract.java
基于Spring MVC的協(xié)議規(guī)范SpringMvcContract:
當前Spring Cloud 微服務解決方案中,為了降低學習成本,采用了Spring MVC的部分注解來完成 請求協(xié)議解析,也就是說 ,寫客戶端請求接口和像寫服務端代碼一樣:客戶端和服務端可以通過SDK的方式進行約定,客戶端只需要引入服務端發(fā)布的SDK API,就可以使用面向接口的編碼方式對接服務:
我們團隊內(nèi)部就是按照這種思路,結(jié)合Spring Boot Starter 的特性,定義了服務端starter,
服務消費者在使用的時候,只需要引入Starter,就可以調(diào)用服務。這個比較適合平臺無關性,接口抽象出來的好處就是可以根據(jù)服務調(diào)用實現(xiàn)方式自有切換:
- 可以基于簡單的Http服務調(diào)用;
- 可以基于Spring Cloud 微服務架構(gòu)調(diào)用;
- 可以基于Dubbo SOA服務治理
這種模式比較適合在SaSS混合軟件服務的模式下自有切換,根據(jù)客戶的硬件能力選擇合適的方式部署,也可以基于自身的服務集群部署微服務
至于Spring Cloud 是如何實現(xiàn) 協(xié)議解析的,可參考代碼:
https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java
當然,目前的Spring MVC的注解并不是可以完全使用的,有一些注解并不支持,如@GetMapping,@PutMapping 等,僅支持使用@RequestMapping 等,另外注解繼承性方面也有些問題;具體限制細節(jié),每個版本能會有些出入,可以參考上述的代碼實現(xiàn),比較簡單。
Spring Cloud 沒有基于Spring MVC 全部注解來做Feign 客戶端注解協(xié)議解析,個人認為這個是一個不小的坑。在剛?cè)胧諷pring Cloud 的時候,就碰到這個問題。后來是深入代碼才解決的.... 這個應該有人寫了增強類來處理,暫且不表,先MARK一下,是一個開源代碼練手的好機會。
PHASE 3. 基于 RequestBean,動態(tài)生成Request
根據(jù)傳入的Bean對象和注解信息,從中提取出相應的值,來構(gòu)造Http Request 對象:
PHASE 4. 使用Encoder 將Bean轉(zhuǎn)換成 Http報文正文(消息解析和轉(zhuǎn)碼邏輯)
Feign 最終會將請求轉(zhuǎn)換成Http 消息發(fā)送出去,傳入的請求對象最終會解析成消息體,如下所示:
在接口定義上Feign做的比較簡單,抽象出了Encoder 和decoder 接口:
目前Feign 有以下實現(xiàn):
PHASE 5. 攔截器負責對請求和返回進行裝飾處理
在請求轉(zhuǎn)換的過程中,Feign 抽象出來了攔截器接口,用于用戶自定義對請求的操作:
比如,如果希望Http消息傳遞過程中被壓縮,可以定義一個請求攔截器:
PHASE 6. 日志記錄
在發(fā)送和接收請求的時候,Feign定義了統(tǒng)一的日志門面來輸出日志信息 , 并且將日志的輸出定義了四個等級:
PHASE 7 . 基于重試器發(fā)送HTTP請求
Feign 內(nèi)置了一個重試器,當HTTP請求出現(xiàn)IO異常時,Feign會有一個最大嘗試次數(shù)發(fā)送請求,以下是Feign核心
代碼邏輯:
重試器有如下幾個控制參數(shù):
具體的代碼實現(xiàn)可參考:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Retryer.java
PHASE 8. 發(fā)送Http請求
Feign 真正發(fā)送HTTP請求是委托給 feign.Client 來做的:
Feign 默認底層通過JDK 的 java.net.HttpURLConnection 實現(xiàn)了feign.Client接口類,在每次發(fā)送請求的時候,都會創(chuàng)建新的HttpURLConnection 鏈接,這也就是為什么默認情況下Feign的性能很差的原因。可以通過拓展該接口,使用Apache HttpClient 或者OkHttp3等基于連接池的高性能Http客戶端,我們項目內(nèi)部使用的就是OkHttp3作為Http 客戶端。
如下是Feign 的默認實現(xiàn),供參考:
Feign 的性能怎么樣?
Feign 整體框架非常小巧,在處理請求轉(zhuǎn)換和消息解析的過程中,基本上沒什么時間消耗。真正影響性能的,是處理Http請求的環(huán)節(jié)。
如上所述,由于默認情況下,Feign采用的是JDK的HttpURLConnection,所以整體性能并不高,剛開始接觸Spring Cloud 的同學,如果沒注意這些細節(jié),可能會對Spring Cloud 有很大的偏見。
我們項目內(nèi)部使用的是OkHttp3 作為連接客戶端。
https://www.jianshu.com/p/8c7b92b4396c
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud Feign设计原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 透露一下Java软件工程师面试常见问题集
- 下一篇: 精通Spring Boot—— 第二十一