Spring Cloud 之 OpenFeign
1、Feign 簡介
Spring Cloud OpenFeign是一套基于Netflix Feign實作的聲明式、模闆化的服務調用用戶端,Feign可以幫助我們更快捷、優雅地調用HTTP API,它使得編寫Web服務用戶端變得更加簡單。我們隻需要通過建立接口并用注解來配置它既可完成對Web服務接口的綁定。它具備可插拔的注解支援,包括Feign注解、JAX-RS注解。它也支援可插拔的編碼器和解碼器。Spring Cloud Feign還擴充了對Spring MVC注解的支援,同時還整合了Ribbon和Eureka來提供均衡負載的HTTP用戶端實作。
官方文檔:《Declarative REST Client: Feign》。
2、簡單使用
在前面學習《Eureka注冊中心》和《用戶端負載均衡》中,我們一直使用了RestTemplate進行服務接口的調用,這個時候我們需要知道服務接口的具體資訊,比如:IP位址或服務名、端口号、接口位址等,這種使用近似原始Http方式的調動,為我們編寫代碼帶來了很大的複雜度。而Feign就是封裝了這些細節,為我們提供了更加快捷和優雅的調用HTTP API的方法,我們下面拭目以待:
1、建構基于Feign的消費者用戶端
注冊中心和服務提供者可以使用前面例子中的應用,這裡隻需要重構消費者引用即可,因為Feign本身就是為了簡化開發者編寫消費服務接口的HTTP封裝。
2、引入Feign依賴
在原來的消費基礎上,增加Feign相關依賴即可,如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
3、開啟Feign功能
開啟Feign功能很簡單,隻需要在啟動類中添加@EnableFeignClients注解接口,這樣就啟動了Feign功能。
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class EurekaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
}
4、定義Feign接口
定義Feign接口是實作Feign調用非常重要和基礎的工作,Feign就是根據定義的接口和其中的注解實作了服務接口的調用,同時目前應用隻需要像本地類一樣調用對應的方法即可。
@FeignClient("eureka-provider")
public interface FeignApiClient {
@GetMapping("/provider")
String provider();
}
在上述代碼中,@FeignClient注解作用目标在接口上,用來表示該接口作為一個FeignClient使用,其中的value,表示FeignClient的名稱(如果使用了服務注冊中心,一般使用服務提供者的名稱),如果項目使用了Ribbon,value屬性會作為微服務的名稱,用于服務發現。而@GetMapping注解,主要用來指定在對應的服務提供者中的HTTP API,即該方法被調用時,實際調用服務提供者的位址。
5、編寫消費者Controller
這裡隻需要一個定義一個普通的Controller類即可,其中需要把前面定義的Feign接口類FeignApiClient注入到該類中即可,這個時候,我們就可以像使用普通的service類一樣,調用該接口的指定方法了。
@RestController
public class FeignController {
Logger logger = Logger.getLogger(FeignController.class);
@Autowired
private FeignApiClient feignApiClient;
@RequestMapping("/feignApi")
public String feignApi(){
logger.info("執行消費者FeignController的feignApi()方法!");
return feignApiClient.provider();
}
}
6、啟動、驗證
啟動應用,正常啟動後,通路http://localhost:8020/feignApi,這個時候,服務提供者的接口就被正常調用了,消費者也傳回了正确的資訊。這個時候,Feign的簡單執行個體我們就完成了,這裡我們隻是簡單了建構了一個基于Feign的消費者,我們下面再嘗試一些其他的用法。
3、 Feign日志級别
3.1、單個服務的日志配置
啟用Feign日志配置需要兩步:
第一步,在配置檔案application.properties中增加相關配置:
# 啟用Feign日志級别
logging.level.com.qriver.cloud.consumer.feign.FeignApiClient: DEBUG
其中,logging.level是固定字首,com.qriver.cloud.consumer.feign.FeignApiClient是對應的Feign接口的權限定類名,對應的Value值隻能是DEBUG類型。
第二步,增加Feign日志的配置類或增加相應配置。
如果使用配置檔案形式,在配置檔案application.properties中增加下面配置即可,其中feign.client.config是固定格式,eureka-provider是服務提供者的應用名稱,logger-level固定格式,full是選用的日志級别。
feign.client.config.eureka-provider.logger-level: full
如果選擇增加Feign日志的配置類的方式,代碼如下:
@Configuration
public class FeignLoggingConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
在上述配置類中,我們使用了FULL級别的日志,無論使用配置檔案還是配置類的方式,這種都是針對每一個微服務的服務提供者進行配置的。
3.2、全局日志配置
Feign日志進行全局配置時,也是需要兩步:
第一步,定義需要輸出日志的地方,這個和java web中普通日志方式是一樣的,即配置需要列印日志的包配置。
# 啟用Feign日志級别
logging.level.com.qriver.cloud.consumer.feign.*: DEBUG
第二步,增加Feign全局日志配置,和單個服務配置類似,也有配置類和配置檔案兩種方式。
如果使用配置檔案方式,隻需要在配置檔案中增加如下配置即可:
feign.client.config.default.logger-level: full
和單個服務提供者日志配置相比,這裡把服務名稱改成default即可。
如果選擇使用配置類,配置類的定義方法和前面為單個服務定義的配置類是一樣的。隻需要把@Configuration注解移除就可以了,這裡不再重複貼出。全局日志配置的引入方式,需要修改啟用Feign的注解@EnableFeignClients,增加defaultConfiguration屬性,并把配置類引入即可,代碼如下:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(defaultConfiguration = FeignLoggingConfig.class)
public class EurekaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
}
3.3、Feign日志級别
Feign一共有四種級别的日志,分别是:
- NONE: 不記錄任何日志資訊,這是預設值。
- BASIC:僅記錄請求的方法,URL及響應狀态和執行時間,類似于Tomcat預設的通路日志。
- HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭資訊
- FULL:記錄了所有請求和響應的明細,包括頭資訊、請求體和中繼資料等。
下面貼出來四種不同級别,列印的日志示例:
FULL級别:
2020-11-08 14:53:07.697 INFO 2504 --- [nio-8020-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client eureka-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=eureka-provider,current list of Servers=[localhost:8010],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:localhost:8010; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springf[email protected]66e383de
2020-11-08 14:53:07.830 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] <--- HTTP/1.1 200 (490ms)
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] connection: keep-alive
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] content-length: 12
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] content-type: text/plain;charset=UTF-8
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] date: Sun, 08 Nov 2020 06:53:07 GMT
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] keep-alive: timeout=60
2020-11-08 14:53:07.831 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider]
2020-11-08 14:53:07.833 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] Hello World!
2020-11-08 14:53:07.834 DEBUG 2504 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] <--- END HTTP (12-byte body)
2020-11-08 14:53:08.661 INFO 2504 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: eureka-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
HEADERS級别:
2020-11-08 14:55:45.091 INFO 10800 --- [nio-8020-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client eureka-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=eureka-provider,current list of Servers=[localhost:8010],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:localhost:8010; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springf[email protected]7e8dcbf2
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] <--- HTTP/1.1 200 (375ms)
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] connection: keep-alive
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] content-length: 12
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] content-type: text/plain;charset=UTF-8
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] date: Sun, 08 Nov 2020 06:55:45 GMT
2020-11-08 14:55:45.179 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] keep-alive: timeout=60
2020-11-08 14:55:45.180 DEBUG 10800 --- [nio-8020-exec-1] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] <--- END HTTP (12-byte body)
2020-11-08 14:55:46.061 INFO 10800 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: eureka-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
BASIC級别:
2020-11-08 14:59:01.149 INFO 94780 --- [nio-8020-exec-2] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client eureka-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=eureka-provider,current list of Servers=[localhost:8010],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:localhost:8010; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springf[email protected]23aa7540
2020-11-08 14:59:01.239 DEBUG 94780 --- [nio-8020-exec-2] c.q.cloud.consumer.feign.FeignApiClient : [FeignApiClient#provider] <--- HTTP/1.1 200 (393ms)
2020-11-08 14:59:02.117 INFO 94780 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: eureka-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
NONE級别:
2020-11-08 15:00:40.790 INFO 44616 --- [nio-8020-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client eureka-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=eureka-provider,current list of Servers=[localhost:8010],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:localhost:8010; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springf[email protected]739c9f13
2020-11-08 15:00:41.759 INFO 44616 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: eureka-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
4、壓縮請求和響應資料
為了提高請求資料和響應資料在網絡中的傳輸效率,Feign提供了對請求和響應的資料的壓縮功能。開啟方式如下:
feign.compression.request.enabled=true
feign.compression.response.enabled=true
那些請求或響應可以被壓縮?Feign提供了兩種政策,第一種是根據多媒體類型,第二種是根據請求的大小,具體配置如下:
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
5、基于Ribbon的負載均衡
Feign實作基于Ribbon的負載均衡非常容易。首先,需要引入Ribbon的依賴檔案,如下所示:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
然後,在定義Feign接口的時候,@FeignClient注解name或value屬性使用服務名稱(不綁定具體執行個體位址即可)即可。
5.1、準備環境
驗證所需的環境和《用戶端負載均衡》中所需的環境一樣,然後重構消費者就可以了,而且消費者隻需要引入ribbon依賴,Feign就會自動的啟用負載均衡機制。
5.2、驗證
啟動後,通過多次通路接口,就會發現服務提供者均勻的分别到了兩個服務上,說明負載均衡生效了。這裡隻是簡單的實作了負載均衡,我們後面再詳細分析他們是如何進行工作的。