一、概述
前面的文章中提到,如果我們要呼叫其它應用的服務,只能夠通過 RestTemplate 的方式,這在我們實際的開發中很不方便。那麼有沒有類似於 Dubbo 中 @Reference 這樣的註解直接呼叫呢?這就是我們今天要講的 Spring Cloud Feign。
Spring Cloud Feign 基於 Netflix Feign 實現,整合了 Spring Cloud Ribbon 與 Spring Cloud Hystrix,除了提供這兩者的強大功能之外,它還提供了一種宣告式的 Web 服務客戶端定義方式。
Spring Cloud Feign 具備可插拔的註解支援,包括 Feign 註解和 JAX-RS 註解。同時,為了適應 Spring 的廣大使用者,它在 Netflix Feign 的基礎上擴充套件了對 Spring MVC 的註解支援。
二、Feign 實戰
SpringBoot 版本號:2.1.6.RELEASE
SpringCloud 版本號:Greenwich.RELEASE
1. pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
2. application.yml
server:
port: 2032
spring:
application:
name: cloud-feign-consumer
eureka:
client:
service-url:
defaultZone: http://user:password@localhost:1111/eureka/
3. FeignApplication.java
// 開啟 Spring Cloud Feign 的支援功能
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
4. 介面定義
@FeignClient(value = "cloud-eureka-client")
public interface FeignService {
@RequestMapping("/hello")
String hello();
}
這裡我們定義了一個介面(方法名和註解與服務提供方一致),並通過 @FeignClient 註解繫結到服務提供方。當呼叫 FeignService.hello() 的時候,Feign 會把請求包裝成 "http://cloud-eureka-client/hello" 的樣子,通過 RestTemplate 呼叫並返回結果。
對於 Spring Cloud Feign 的引數繫結,就是當呼叫方法需要有引數的時候,引數格式只需按照 Spring MVC 的註解即可。區別是:在定義各引數繫結時,@RequestParam、@RequestHeader 等可以指定引數名稱的註解,它們的 value 千萬不能少。在 SpringMVC 程式中,這些註解會根據引數名來作為預設值,但是在 Feign 中繫結引數必須通過 value 屬性來指明具體的引數名。
對於在服務提供方和服務呼叫方都維護一份介面定義的做法著實不提倡,原因很簡單,修改了一個方法,需要同時在兩個地方做出改變。比較好的做法是:服務提供方暴露一個 xxx-interface 的 jar 包供服務呼叫方引用。這樣,服務呼叫方,直接引用 xxx-interface 的方法,不維護介面定義,不關心實現。
5. Spring Cloud Feign 中的 Ribbon 配置
由於 Spring Cloud Feign 的客戶端負載均衡是通過 Spring Cloud Ribbon 實現的,所以我們可以直接通過配置 Ribbon 客戶端的方式來定義各個服務客戶端呼叫的引數。
# 全域性配置
ribbon:
# 連線超時時間
ConnectTimeout: 500
# 呼叫超時時間
ReadTimeout: 2000
# 針對單個服務的 Ribbon 配置
cloud-eureka-client:
ribbon:
# 重試次數
MaxAutoRetries: 2
我們需要讓 Hystrix 的超時時間大於 Ribbon 的超時時間,否則 Hystrix 命令超時後,該命令直接熔斷,重試機制就沒有任何意義了。
配置引數可以在 CommonClientConfigKey.java 中查詢到,具體每個引數的含義,就不在這裡細講了。
6. Spring Cloud Feign 中的 Hystrix 配置
預設情況下,Spring Cloud Feign 會為將所有 Feign 客戶端的方法都封裝到 Hystrix 命令中進行服務保護。當然,可以在配置檔案中選擇開啟或者關閉 Hystrix:
feign:
hystrix:
enabled: true
如果我們僅要在某個服務中關閉 Hystrix 呢?那麼我們就要自定義一個配置類了:
@Configuration
public class DisableHystrixConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Feign.Builder feignBuilder() {
return new Feign.Builder();
}
}
然後再 @FeignClient 註解中引用:
@FeignClient(value = "cloud-eureka-client", configuration = DisableHystrixConfig.class)
上一篇文章 我們看到 Hystrix 中有很多的配置引數,那麼在 Feign 中如何配置它們呢?
# 配置全域性的超時時間
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutinMilliseconds: 5000
# 針對某個 commandKey 做配置,而 commandKey 預設取得是客戶端中的方法名作為標識。所以如果存在相同方法名會共用配置。
hello:
execution:
isolation:
thread:
timeoutinMilliseconds: 5000
Hystrix 的配置引數可以在 HystrixCommandProperties.java 中找到。
7. 服務降級
在 Hystrix 中服務降級我們通過 fallbackMethod 來實現,那麼 Feign 中沒法直接使用 @HystrixCommand 註解,要怎麼配置服務降級呢?首先。我們要實現一個需要降級的介面,並提供降級實現:
@Component
public class FeignServiceFallback implements FeignService {
@Override
public String hello() {
return "error";
}
}
然後再 @FeignClient 註解中引用:
@FeignClient(value = "cloud-eureka-client", configuration = DisableHystrixConfig.class, fallback = FeignServiceFallback.class)
8. 日誌配置
Spring Cloud Feign 在構建被 @FeignClient 註解修飾的服務客戶端時,會為每一個客戶端都建立一個 Logger.Level 例項,我們可以利用該日誌物件的 DEBUG 模式來幫助分析 Feign 的請求細節。
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
Logger.Level 有四種級別:
- NONE: 不記錄任何資訊。
- BASIC: 僅記錄請求方法、URL以及響應狀態碼和執行時間。
- HEADERS: 除了記錄 BASIC 級別的資訊之外,還會記錄請求和響應的頭資訊。
- FULL: 記錄所有請求與響應的明細,包括頭資訊、請求體、後設資料等。
tips:該配置項只有 Spring 的日誌級別為 Debug 時才生效。
9. 其他配置
Spring Cloud Feign 支援對請求與響應進行 GZIP 壓縮,以減少通訊過程中的效能損耗。
feign:
compression:
# 開啟請求和響應的壓縮功能
request:
enabled: true
# 超過 2M 才開始壓縮
min-request-size: 2048
# 壓縮型別
mime-types: {"text/xml", "application/xml", "application/json"}
response:
enabled: true