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
- 引入依賴
在order-service服務的pom檔案中引入feign的依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
- 添加註解
在order-service的啟動類添加註解開啟Feign的功能
- 編寫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來發送了。
- 用Feign客戶端代替RestTemplate
修改order-service中的OrderService類中的queryOrderById方法,使用Feign客戶端代替RestTemplate
- 測試
-
首先啟動本地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:配置檔案
- 區域性生效
基於配置檔案修改feign的日誌級別可以針對單個服務:
feign:
client:
config:
userservice: # 針對某個微服務的配置
loggerLevel: FULL # 日誌級別
- 全域性生效
也可以針對所有服務:
feign:
client:
config:
default: # 這裡用default就是全域性配置,如果是寫服務名稱,則是針對某個微服務的配置
loggerLevel: FULL # 日誌級別
- 日誌
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)
- 引入依賴
在order-service的pom檔案中引入Apache的HttpClient依賴:
<!--httpClient的依賴 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 配置連線池
在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:
3.3 小結
Feign的優化:
- 日誌級別儘量用basic
- 使用HttpClient或OKHttp代替URLConnection
- 引入feign-httpClient依賴
- 配置檔案開啟httpClient功能,設定連線池引數
4 最佳實踐
所謂最佳實踐,就是使用過程中總結的經驗,最好的一種使用方式。
通過觀察可以發現,Feign的客戶端(order-service中)與服務提供者的controller(user-service中)程式碼非常相似:
有沒有一種辦法簡化這種重複的程式碼編寫呢?
4.1 最佳實踐1:繼承方式
優點:
- 簡單
- 實現了程式碼共享
缺點:
- 服務提供方、服務消費方緊耦合
- 引數列表中的註解對映並不會繼承,因此Controller中必須再次宣告方法、引數列表、註解
4.2 最佳實踐2:抽取方式
- 傳統實現:
- 優化實現:
例如,將UserClient、User、Feign的預設配置都抽取到一個feign-api包中,所有微服務引用該依賴包,即可直接使用。
4.3 實現基於抽取的最佳實踐(方式2)
步驟:
4.3.1 抽取(新建feign-api模組)
- 在當前cloud-demo專案下建立一個module,命名為feign-api
- 在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模組
- order-service模組中的UserClient、User、DefaultFeignConfiguration刪掉
注意:此處本地未刪,僅將他們更名為:原名2
- 在order-service的pom檔案中中引入feign-api的依賴
<dependency>
<groupId>cn.itcast.demo</groupId>
<artifactId>feign-api</artifactId>
<version>1.0</version>
</dependency>
- 修改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 解決啟動報錯:掃描包問題
- 當定義的FeignClient不在SpringBootApplication的掃描包範圍時,這些FeignClient無法使用。有兩種方式解決:
- 方式一:指定Feign應該掃描的包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
- 方式二:指定需要載入的Client介面(推薦)
@EnableFeignClients(clients = {UserClient.class})
- 解決跨模組依賴注入問題(掃描包問題)後,重啟測試
可以看到此時,OrderService中注入的userClient編譯正常了,不再報錯
重啟OrderApplication服務,瀏覽器訪問訂單101測試ok
4.4 小結