Feign遠端呼叫 (介紹與使用)

2023-02-10 15:00:24

Feign遠端呼叫

Feign是代替RestTemplate進行遠端呼叫的元件,避免了RestTemplate手寫複雜的url容易出錯的問題,並提高程式碼的可讀性

使用Feign步驟

1)引入依賴

哪個服務要傳送遠端請求就匯入在哪個服務

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2)新增註解

在order-service的啟動類新增註解 @EnableFeignClients 開啟Feign的功能:

3)編寫Feign的使用者端

在order-service中新建一個介面UserClient,內容如下:①加註解 ②寫url語句

@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

這個使用者端主要是基於SpringMVC的註解來宣告遠端呼叫的資訊,比如:

  • 服務名稱:userservice
  • 請求方式:GET
  • 請求路徑:/user/
  • 請求引數:Long id
  • 返回值型別:User

4)呼叫介面

注入介面類物件,呼叫介面類的方法

自定義設定

Feign可以支援很多的自定義設定,如下表所示:(一般只會修改紀錄檔級別來查bug,其他不改)

型別 作用 說明
feign.Logger.Level 修改紀錄檔級別 包含四種不同的級別:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 響應結果的解析器 http遠端呼叫的結果做解析,例如解析json字串為java物件
feign.codec.Encoder 請求引數編碼 將請求引數編碼,便於通過http請求傳送
feign. Contract 支援的註解格式 預設是SpringMVC的註解
feign. Retryer 失敗重試機制 請求失敗的重試機制,預設是沒有,不過會使用Ribbon的重試

一般情況下,預設值就能滿足我們使用,如果要自定義時,只需要建立自定義的@Bean覆蓋預設Bean即可。

下面以紀錄檔為例來演示如何自定義設定。

1)組態檔方式

基於組態檔修改feign的紀錄檔級別可以針對單個服務:只有通過當前服務中存取userservice服務時紀錄檔會列印

feign:  
  client:
    config: 
      userservice: # 針對某個微服務的設定
        loggerLevel: FULL #  紀錄檔級別 

也可以針對所有服務:通過當前服務存取的all服務都會列印紀錄檔

feign:  
  client:
    config: 
      default: # 這裡用default就是全域性設定,如果是寫服務名稱,則是針對某個微服務的設定
        loggerLevel: FULL #  紀錄檔級別 

而紀錄檔的級別分為四種:

不建議FULL,因為會影響效能。一般是BASIC、NONE(預設)

  • NONE:不記錄任何紀錄檔資訊,這是預設值
  • BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
  • HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭資訊
  • FULL:記錄所有請求和響應的明細,包括頭資訊、請求體、後設資料。

2)Java程式碼方式

也可以基於Java程式碼來修改紀錄檔級別,先宣告一個類,然後宣告一個Logger.Level的物件:

該類沒有注入到bean所以暫時不起作用。只有當被其他類寫入到註解中才生效

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 紀錄檔級別為BASIC
    }
}

如果要全域性生效,將其放到啟動類的@EnableFeignClients這個註解中:

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 

如果是區域性生效,則把它放到對應的@FeignClient這個註解中:

@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

Feign優化使用

總結,Feign的優化:

1.紀錄檔級別儘量用basic

2.使用HttpClient或OKHttp代替URLConnection

① 引入feign-httpClient依賴

② 組態檔開啟httpClient功能,設定連線池引數

Feign底層發起http請求,依賴於其它的框架。其底層使用者端實現包括:

•URLConnection:預設實現,不支援連線池

•Apache HttpClient :支援連線池

•OKHttp:支援連線池

因此提高Feign的效能主要手段就是使用連線池代替預設的URLConnection。

這裡我們用Apache的HttpClient來演示。

1)引入依賴

在order-service的pom檔案中引入Apache的HttpClient依賴:

<!--httpClient的依賴 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2)設定連線池

在order-service的application.yml中新增設定:

feign:
  client:
    config:
      default: # default全域性的設定
        loggerLevel: BASIC # 紀錄檔級別,BASIC就是基本的請求和響應資訊
  httpclient:
    enabled: true # 開啟feign對HttpClient的支援
    max-connections: 200 # 最大的連線數
    max-connections-per-route: 50 # 每個路徑的最大連線數

測試:

接下來,在FeignClientFactoryBean中的loadBalance方法中打斷點:

Debug方式啟動order-service服務,可以看到這裡的client,底層就是Apache HttpClient:

最佳實踐

所謂最近實踐,就是使用過程中總結的經驗,最好的一種使用方式。

自習觀察可以發現,Feign的使用者端與服務提供者的controller程式碼非常相似:

feign使用者端:

UserController:

有沒有一種辦法簡化這種重複的程式碼編寫呢?

1、繼承方式【不建議】

一樣的程式碼可以通過繼承來共用:

1)定義一個API介面,利用定義方法,並基於SpringMVC註解做宣告。

2)Feign使用者端和Controller都整合改介面

優點:

  • 簡單
  • 實現了程式碼共用

缺點:

  • 服務提供方、服務消費方緊耦合

  • 參數列中的註解對映並不會繼承,因此Controller中必須再次宣告方法、參數列、註解

2、抽取方式

就是將每一個功能業務寫成一個小的微服務,然後其他的大業務可以匯入依賴呼叫這些小的微服務,從而減少重複寫同樣的功能程式碼

將Feign的Client抽取為獨立模組,並且把介面有關的POJO、預設的Feign設定都放到這個模組中,提供給所有消費者使用。

例如,將UserClient、User、Feign的預設設定都抽取到一個feign-api包中,所有微服務參照該依賴包,即可直接使用。

3、 實現抽取的最佳實踐

1)抽取

首先建立一個module,命名為feign-api:

專案結構:

在feign-api中然後引入feign的starter依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然後,order-service中編寫的UserClient、User、DefaultFeignConfiguration都複製到feign-api專案中

2)在order-service中使用feign-api

首先,刪除order-service中的UserClient、User、DefaultFeignConfiguration等類或介面。

在order-service的pom檔案中中引入feign-api的依賴:

<dependency>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>

修改order-service中的所有與上述三個元件有關的導包部分,改成匯入feign-api中的包

3)重啟測試

重啟後,發現服務報錯了:

這是因為UserClient現在在cn.itcast.feign.clients包下,而order-service的@EnableFeignClients註解是在cn.itcast.order包下,不在同一個包,無法掃描到UserClient。

4)解決跨服務掃描包問題

在啟動類上新增註解屬性

方式一:

指定Feign應該掃描的包:

@EnableFeignClients(basePackages = "cn.itcast.feign.clients")

方式二:

指定需要載入的Client介面:這裡是陣列可以放多個引數

@EnableFeignClients(clients = {UserClient.class})