1. 程式人生 > >Spring Cloud Feign作為HTTP客戶端呼叫遠端HTTP服務,feign熔斷器

Spring Cloud Feign作為HTTP客戶端呼叫遠端HTTP服務,feign熔斷器

在Spring Cloud Netflix棧中,各個微服務都是以HTTP介面的形式暴露自身服務的,因此在呼叫遠端服務時就必須使用HTTP客戶端。我們可以使用JDK原生的URLConnection、Apache的Http Client、Netty的非同步HTTP Client, Spring的RestTemplate。但是,用起來最方便、最優雅的還是要屬Feign了。

Feign簡介

Feign是一種宣告式、模板化的HTTP客戶端。在Spring Cloud中使用Feign, 我們可以做到使用HTTP請求遠端服務時能與呼叫本地方法一樣的編碼體驗,開發者完全感知不到這是遠端方法,更感知不到這是個HTTP請求。比如:

@Autowired
private AdvertGropRemoteService service; // 遠端服務

public AdvertGroupVO foo(Integer groupId) {
    return service.findByGroupId(groupId); // 通過HTTP呼叫遠端服務
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

開發者通過service.findByGroupId()就能完成傳送HTTP請求和解碼HTTP返回結果並封裝成物件的過程。

Feign的定義

為了讓Feign知道在呼叫方法時應該向哪個地址發請求以及請求需要帶哪些引數,我們需要定義一個介面:

@FeignClient
(name = "ea") // [A] public interface AdvertGroupRemoteService { @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) // [B] AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId) // [C] @RequestMapping(value = "/group/{groupId}", method = RequestMethod.PUT) void
update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

A: @FeignClient用於通知Feign元件對該介面進行代理(不需要編寫介面實現),使用者可直接通過@Autowired注入。

B: @RequestMapping表示在呼叫該方法時需要向/group/{groupId}傳送GET請求。

C: @PathVariableSpringMVC中對應註解含義相同。

Spring Cloud應用在啟動時,Feign會掃描標有@FeignClient註解的介面,生成代理,並註冊到Spring容器中。生成代理時Feign會為每個介面方法建立一個RequetTemplate物件,該物件封裝了HTTP請求需要的全部資訊,請求引數名、請求方法等資訊都是在這個過程中確定的,Feign的模板化就體現在這裡。

在本例中,我們將Feign與Eureka和Ribbon組合使用,@FeignClient(name = "ea")意為通知Feign在呼叫該介面方法時要向Eureka中查詢名為ea的服務,從而得到服務URL。

Feign的Encoder、Decoder和ErrorDecoder

Feign將方法簽名中方法引數物件序列化為請求引數放到HTTP請求中的過程,是由編碼器(Encoder)完成的。同理,將HTTP響應資料反序列化為java物件是由解碼器(Decoder)完成的。

預設情況下,Feign會將標有@RequestParam註解的引數轉換成字串新增到URL中,將沒有註解的引數通過Jackson轉換成json放到請求體中。注意,如果在@RequetMapping中的method將請求方式指定為POST,那麼所有未標註解的引數將會被忽略,例如:

@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
  • 1
  • 2

此時因為宣告的是GET請求沒有請求體,所以obj引數就會被忽略。

在Spring Cloud環境下,Feign的Encoder*只會用來編碼沒有添加註解的引數*。如果你自定義了Encoder, 那麼只有在編碼obj引數時才會呼叫你的Encoder。對於Decoder, 預設會委託給SpringMVC中的MappingJackson2HttpMessageConverter類進行解碼。只有當狀態碼不在200 ~ 300之間時ErrorDecoder才會被呼叫。ErrorDecoder的作用是可以根據HTTP響應資訊返回一個異常,該異常可以在呼叫Feign介面的地方被捕獲到。我們目前就通過ErrorDecoder來使Feign介面丟擲業務異常以供呼叫者處理。

Feign的HTTP Client

Feign在預設情況下使用的是JDK原生的URLConnection傳送HTTP請求,沒有連線池,但是對每個地址會保持一個長連線,即利用HTTP的persistence connection 。我們可以用Apache的HTTP Client替換Feign原始的http client, 從而獲取連線池、超時時間等與效能息息相關的控制能力。Spring Cloud從Brixtion.SR5版本開始支援這種替換,首先在專案中宣告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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

然後在application.properties中新增:

feign.httpclient.enabled=true
  • 1

總結

通過Feign, 我們能把HTTP遠端呼叫對開發者完全透明,得到與呼叫本地方法一致的編碼體驗。這一點與阿里Dubbo中暴露遠端服務的方式類似,區別在於Dubbo是基於私有二進位制協議,而Feign本質上還是個HTTP客戶端。如果是在用Spring Cloud Netflix搭建微服務,那麼Feign無疑是最佳選擇。