聲明式HTTP客戶端-Feign 使用入門詳解
什麼是 OpenFeign
OpenFeign (以下統一簡稱為 Feign) 是 Netflix 開源的聲明式 HTTP 客戶端,集成了 Ribbon 的負載均衡、輪詢演算法和 RestTemplate 的 HTTP 調用等特性,並對其進行封裝,使用者只需要在此基礎上,定義一個介面,並在介面上標註一個 FeignClient
,便可以實現 HTTP 遠程調用,上面的聲明式 HTTP 如何理解,可以理解為
只需要聲明一個介面,Feign 就會通過你定義的介面,自動給你構造請求的目標地址並請求。
下面介紹下如何在項目中集成 Feign 組件,只需要遵循 SpringBoot 開發三板斧(1、加依賴,2、加註解,3、加配置)即可
環境準備
-
加依賴
openfeign
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
加註解
@EnableFeignClients
@SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
加配置(由於 feign 不需要額外在 application.yml 或者 application.properties)中配置,只需要配置好調用微服務的名稱和埠即可,下面舉個例子,user-center 就是我們調用的微服務
server: port: 8081 spring: cloud: nacos: discovery: server-addr: localhost:8848 # 微服務名稱 application: name: user
新建 OpenFeign 介面
@FeignClient(name = "user")
public interface UserFeignClient {
@GetMapping("/users/{id}")
UserDto findById(@PathVariable Integer id);
}
新建 Controller 層
@Service
public class ArticleController {
@Resource
private UserFeignClient userFeignClient;
public ArticleDto findByUserId(Integer userId) {
... ...
ArticleDto articleDto = this.userFeignClient.findById(userId);
... ...
return articleDto;
}
}
以上,就是 openFeign 的基本使用入門了
Feign的日誌配置
由於 Feign 在沒有配置的情況下是不會列印任何日誌,如果想要看到 Feign 的日誌,需要額外的配置;但是在此之前,我們先了解下 Feign 的自定義日誌級別。
Feign 的自定義日誌級別
級別 | 列印內容 |
---|---|
NONE(默認值) | 不記錄任何日誌 |
BASIC | 僅記錄請求方法、URL、響應狀態碼以及執行時間 |
HEADERS | 記錄 BASIC 級別的基礎上,記錄請求和響應的 header |
FULL | 記錄請求和響應的 header、body 和元數據 |
Feign 的自定義日誌級別可以通過 Java 程式碼方式或配置屬性方式來實現,下面我們先介紹下程式碼的實現方式
程式碼配置方式
-
編寫一個 Configuration 的類
public class UserFeignConfiguration { @Bean public Logger.Level level() { // 生產上不建議使用FULL,這樣會產生大量的日誌,影響性能的同時還不好定位問題。 // 建議使用 BASIC return Logger.Level.FULL; } }
-
然後在對應的 Feign 介面設置 Configuration 類
@FeignClient(name = "user", configuration = UserFeignConfiguration.class) public interface UserFeignClient { @GetMapping("/users/{id}") UserDto findById(@PathVariable Integer id); }
-
最後還要將 Feign介面類的全路徑設置在 yml 文件裡面
logging: level: # Feign介面類的全路徑 # Feign的日誌級別是建立在Feign的介面 debug 級別之上的 com.example.article.feignclient.UserFeignClient: debug
當調用介面時,輸出日誌結果如下
[UserFeignClient#findById] <--- HTTP/1.1 200 (794ms) [UserFeignClient#findById] content-type: application/json;charset=UTF-8 [UserFeignClient#findById] date: Mon, 29 Aug 2022 10:56:27 GMT [UserFeignClient#findById] transfer-encoding: chunked [UserFeignClient#findById] [UserFeignClient#findById] {"id":1,"username":"張三","createTime":"2022-08-25T17:17:04.000+0000","updateTime":"2022-08-25T17:17:04.000+0000"} [UserFeignClient#findById] <--- END HTTP (180-byte body)
這樣,通過程式碼來設置 Feign 的日誌自定義級別方式就配置好了。但是這樣,並不是完美的解決方案,就相當於每次新建一個 Feign 介面,就要編寫一個對應的 xxxFeignConfiguration 類,然後在 Feign 介面上指定,這樣下來太繁瑣了,下面看下全局的程式碼配置方式:
不需要在 每個 Feign 介面上面都要配置下
configuration = UserFeignConfiguration.class
,我們只需要在 Application 啟動類上面,指定@EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class)
,註:這裡將 UserFeignConfiguration 已經更名為 GlobalFeignConfiguration@SpringBootApplication @EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
屬性配置方式
在 application.yml 加入只需要配置以下的屬性既可以實現 Feign 的自定義級別
feign:
client:
config:
# 微服務名稱
user:
loggerLevel: full
同理,yml 文件也支援 全局的屬性配置方式
feign:
client:
config:
# 全局配置(default 默認就是適用於全部微服務)
default:
loggerLevel: full
Feign的多參數請求構造
GET 請求
請求多參數的URL,如請求地址為 //www.xxx.me/admin/user/get?username=張三&school=陽光小學&birthDay=2012-08-01
,SpringCloud 為 Feign 整合了 SpringMVC 的註解支援
-
@SpringQueryMap
註解@FeignClient("user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get(@SpringQueryMap User user); }
-
@RequestParam
註解(表單傳參)@FeignClient("user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get(@RequestParam("username") String username, @RequestParam("school") String school, @RequestParam("birthDayDay") String birthDay); }
-
@PathVariable
註解(URL攜帶參數)@FeignClient("user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get(@PathVariable("username") String username, @PathVariable("school") String school, @PathVariable("birthDayDay") String birthDay); }
POST 請求
Feign 默認的傳參方式就是 JSON 傳參(@RequestBody
),因此定義介面的時候可以不用@RequestBody
註解標註,但為了開發規範,建議加上
-
@RequestBody
註解@FeignClient("user") public interface UserFeignClient { @RequestMapping(value = "/post", method = RequestMethod.POST) public User post(@RequestBody User user); }
超時設置
我們在通過 Feign 去調用介面,難免會遇到超時的問題,我們可以在 yml 文件設置超時屬性,防止系統拋出超時異常
feign:
client:
config:
# 全局配置(default 默認就是適用於全部微服務)
default:
connectTimeout: 100000
readTimeout: 100000
# 單獨配置
user:
connectTimeout: 300000
readTimeout: 300000
Feign 性能優化
默認情況下,Feign 使用的是 UrlConnetcion 去請求,這種原生的請求方式一旦遇到高並發的情況下,響應會變得很慢,所以我們可以考慮加入連接池技術來優化性能,下面介紹下如何集成 Apache 下的 HttpClient 的連接池
-
加入 httpclient 依賴
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
-
設置 yml 文件屬性
feign: # 這樣就設置好了 feign 請求方式是 httpclient,而不是 UrlConnetcion httpclient: enable: true # feign的最大連接數 max-connection: 200 # feign 單個路徑的最大連接數 max-connections-per-route: 50