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