1. 程式人生 > 其它 >HM-SpringCloud微服務系列2.2【Feign遠端呼叫】

HM-SpringCloud微服務系列2.2【Feign遠端呼叫】

1 Feign替代RestTemplate

1.1 RestTemplate方式呼叫存在的問題

1.2 http客戶端Feign的介紹

Feign是一個宣告式的http客戶端,官方地址:https://github.com/OpenFeign/feign
其作用就是幫助我們優雅的實現http請求的傳送,解決上面提到的問題。

1.3 定義和使用Feign

  1. 引入依賴
    在order-service服務的pom檔案中引入feign的依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

  1. 添加註解
    在order-service的啟動類添加註解開啟Feign的功能

  1. 編寫Feign的客戶端
    在order-service中新建一個介面
package cn.itcast.order.client;

import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

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

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

  • 服務名稱:userservice
  • 請求方式:GET
  • 請求路徑:/user/{id}
  • 請求引數:Long id
  • 返回值型別:User
    這樣,Feign就可以幫助我們傳送http請求,無需自己使用RestTemplate來發送了。

  1. 用Feign客戶端代替RestTemplate
    修改order-service中的OrderService類中的queryOrderById方法,使用Feign客戶端代替RestTemplate



  1. 測試
  • 首先啟動本地nacos(for OrderApplication)

  • 其次啟動本地nacos叢集(for UserApplication & UserApplication2)
    考慮到2.1節中已對UserApplication & UserApplication2進行了叢集搭建,參考https://www.cnblogs.com/yppah/p/15791141.html啟動本地nacos叢集&nginx反向代理負載均衡




  • 啟動3個微服務

注意:OrderApplication參考上節2.1,也將nacos地址改為80埠(與UserApplication & UserApplication2統一);並且將namespace註釋掉,不然報錯



清空UserApplication & UserApplication2的控制檯啟動日誌資訊
測試:瀏覽器訪問http://localhost:8080/order/1014遍




可以看出feign不僅實現了遠端呼叫,而且實現了負載均衡(原因是feign中已經集成了ribbon負載均衡,如下圖)

1.4 小結

使用Feign的步驟:
① 引入依賴
② 新增@EnableFeignClients註解
③ 編寫FeignClient介面
④ 使用FeignClient中定義的方法代替RestTemplate

2 自定義配置


下面以日誌為例來演示如何自定義配置

2.1 方式1:配置檔案

  1. 區域性生效
    基於配置檔案修改feign的日誌級別可以針對單個服務:
feign:  
  client:
    config: 
      userservice: # 針對某個微服務的配置
        loggerLevel: FULL #  日誌級別 
  1. 全域性生效
    也可以針對所有服務:
feign:  
  client:
    config: 
      default: # 這裡用default就是全域性配置,如果是寫服務名稱,則是針對某個微服務的配置
        loggerLevel: FULL #  日誌級別 
  1. 日誌loggerLevel的級別
    • NONE:不記錄任何日誌資訊,這是預設值
    • BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
    • HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭資訊
    • FULL:記錄所有請求和響應的明細,包括頭資訊、請求體、元資料

配置之前清空控制檯日誌,訪問一下http://localhost:8080/order/101


配置全域性,重啟OrderApplication服務,清空控制檯啟動日誌,再訪問一下


日誌輸出沒變化,不知道為啥qaq

2.2 方式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)




重啟服務後,清空控制檯,訪問http://localhost:8080/order/101

ok

2.3 小結

3 Feign效能優化

3.1 Feign底層的客戶端實現&效能優化手段

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

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

因此提高Feign的效能主要手段包括:

  • 使用連線池代替預設的URLConnection
  • 日誌級別,最好用basic或none

3.2 優化示例:連線池配置(Apache HttpClient)

  1. 引入依賴
    在order-service的pom檔案中引入Apache的HttpClient依賴:
<!--httpClient的依賴 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

  1. 配置連線池
    在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 # 每個路徑的最大連線數

  1. 測試
    在FeignClientFactoryBean中的loadBalance方法中打斷點:

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

3.3 小結

Feign的優化:

  1. 日誌級別儘量用basic
  2. 使用HttpClient或OKHttp代替URLConnection
    • 引入feign-httpClient依賴
    • 配置檔案開啟httpClient功能,設定連線池引數

4 最佳實踐

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


通過觀察可以發現,Feign的客戶端(order-service中)與服務提供者的controller(user-service中)程式碼非常相似:

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

4.1 最佳實踐1:繼承方式


優點:

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

缺點:

  • 服務提供方、服務消費方緊耦合
  • 引數列表中的註解對映並不會繼承,因此Controller中必須再次宣告方法、引數列表、註解

4.2 最佳實踐2:抽取方式

  1. 傳統實現:
  2. 優化實現:

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

4.3 實現基於抽取的最佳實踐(方式2)

步驟:

4.3.1 抽取(新建feign-api模組)

  1. 在當前cloud-demo專案下建立一個module,命名為feign-api



  2. 在feign-api中引入feign的starter依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


3. order-service模組中編寫的UserClient、User、DefaultFeignConfiguration都複製到feign-api模組中

4.3.2 在order-service中使用feign-api模組

  1. order-service模組中的UserClient、User、DefaultFeignConfiguration刪掉

注意:此處本地未刪,僅將他們更名為:原名2


  1. 在order-service的pom檔案中中引入feign-api的依賴
<dependency>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>
  1. 修改order-service中的所有與上述三個元件有關的導包部分,改成匯入feign-api中的包

可以看到OrderService中注入的userClient編譯異常了,此問題會在4.3.4小節中解決

4.3.3 重啟測試

重啟OrderApplication服務,報錯了
Field userClient in cn.itcast.order.service.OrderService required a bean of type 'cn.itcast.feign.clients.UserClient' that could not be found.

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

掃不到包導致注入失敗,解決方法如下4.3.4

4.3.4 解決啟動報錯:掃描包問題

  1. 當定義的FeignClient不在SpringBootApplication的掃描包範圍時,這些FeignClient無法使用。有兩種方式解決:
  • 方式一:指定Feign應該掃描的包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
  • 方式二:指定需要載入的Client介面(推薦)
@EnableFeignClients(clients = {UserClient.class})
  1. 解決跨模組依賴注入問題(掃描包問題)後,重啟測試


    可以看到此時,OrderService中注入的userClient編譯正常了,不再報錯
    重啟OrderApplication服務,瀏覽器訪問訂單101測試ok

4.4 小結