【Spring Cloud總結】12.覆寫Feign的默認配置

接上篇《11.Feign的簡介及基礎使用》  Spring Cloud版本爲Finchley.SR2版

上一篇我們講了聲明式REST服務調用的組件Feign的介紹以及基本的使用,像之前的Ribbon一樣,Feign也可以修改其默認配置。本篇我們就來探討複寫Feign的默認配置的幾種方式。
本部分官方文檔:https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html#spring-cloud-feign

一、Feign默認配置修改方式

在Spring Cloud的官方文檔中,針對Feign的默認配置覆蓋,有下面的描述:

Spring Cloud的Feign支持的一箇中心概念是命名客戶端。

每一個feign client是整體的一部分,它們一起工作以按需聯繫遠程服務器,並且該整體具有一個名稱,開發人員可以使用@FeignClient將其命名。

Spring Cloud根據需要使用FeignClientsConfiguretion爲每個命名的客戶端創建一個新的整體作爲ApplicationContext。這包含(其他)feign.Decoder,feign.Encoder和feign.Contract。

Spring Cloud允許您通過聲明其他配置(在其上FeignClientsConfiguration)使用來完全控制假裝客戶端@FeignClient。例:

@FeignClient(name =“stores”,configuration = FooConfiguration.class)
 public  interface StoreClient {
     // .. 
}

在這種情況下,客戶端由已經FeignClientsConfiguration與任何in 組成的組件組成FooConfiguration(後者將覆蓋前者)。

看到這裏大家是不是感覺似曾相識?沒錯,之前我們學習的Ribbon也是這種模式!Ribbon的默認配置是RibbonClientConfiguration,Feign的默認配置是FeignClientsConfiguretion;Ribbon的聲明式覆蓋方式是在@RibbonClient註解後添加自定義的configuration,而Feign是在@FeignClient註解後添加自定義的configuration。這樣看來,Feign的配置覆蓋方式和Ribbon配置是一樣的,我們只需要瞭解Feign有哪些我們可以修改的基本配置即可。

在官方文檔中,說明了Feign的默認配置項:
Decoder feignDecoder: ResponseEntityDecoder (爲SpringDecoder的包裝類)
Encoder feignEncoder: SpringEncoder
Logger feignLogger: Slf4jLogger
Contract feignContract: SpringMvcContract
Feign.Builder feignBuilder: HystrixFeign.Builder
Client feignClient: 如果啓用的Ribbon,則爲LoadBalancerFeignClient, 否則使用默認的FeignClient。
這裏Decoder爲請求信息的編碼器,Encoder爲請求信息的解碼器,Logger爲日誌類, Contract爲Feign的契約類(功能是將我們傳入的接口進行解析驗證,看註解的使用是否符合規範,然後返回給我們接口上各種相應的元數據),Builder爲Feign的構造類,然後就是Feign的Client。

我們就可以在自定義的configuration配置類,去重新覆蓋上面的Bean,來實現Feign的默認配置的修改,以符合項目的實際要求。

二、Feign默認配置修改實例

我們回顧一下上一章我們爲movie服務去聲明式調用user工程服務的UserFeignClient接口:

package com.microserver.cloud;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.microserver.cloud.entity.User;

@FeignClient("microserver-provider-user")
public interface UserFeignClient {
    @RequestMapping(value="/findById/{id}", method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id);
    
    @RequestMapping(value="/postUser", method=RequestMethod.POST)
    public String postUser(@RequestBody User user);
}

我們這裏修改@FeignClient,來添加configuration參數進行自定義配置:

import com.microserver.config.TestFeignConfiguration;
@FeignClient(name="microserver-provider-user",configuration = TestFeignConfiguration.class )
public interface UserFeignClient {
    //代碼省略...
}

然後我們在之前的com.microserver.config包下新建TestFeignConfiguration類:

package com.microserver.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestFeignConfiguration {
    
}

這裏將TestFeignConfiguration類放在com.microserver.config包下而不是com.microserver.cloud下的子包,原因和之前Ribbon的配置一樣,是爲了避免在主Application Context的組件掃描下,該配置被所有的Feign客戶端共享。

那麼下面我們來覆蓋Feign的一些配置,這裏我們將Feign的默認配置項中的Contract契約類爲Default類:

package com.microserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Contract;

@Configuration
public class TestFeignConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
}

這裏的意思是,Feign原來的默認Contract契約類爲SpringMvcContract,意味着我們可以使用Spring MVC的服務請求註解(如@RequestMapping註解)來進行聲明式服務的定義,如果我們更換Contract契約類爲Default類,這裏我們只能使用Feign自己的註解,而不能使用Spring MVC的註解了。

我們這裏先不修改UserFeignClient中的@RequestMapping註解,先啓動movie工程看看(Eureka順便也啓起來),是不是FeignClient不再支持Spring MVC的註解了。當啓動起來之後,控制檯出現以下異常:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'movieController': Unsatisfied dependency expressed through field 'userFeignClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.microserver.cloud.UserFeignClient': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Method findById not annotated with HTTP method type (ex. GET, POST)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
    //後續異常省略......
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.microserver.cloud.UserFeignClient': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Method findById not annotated with HTTP method type (ex. GET, POST)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:178) ~[spring-beans-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:101) ~[spring-beans-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    //後續異常省略......
Caused by: java.lang.IllegalStateException: Method findById not annotated with HTTP method type (ex. GET, POST)
    at feign.Util.checkState(Util.java:126) ~[feign-core-9.7.0.jar:na]
    //後續異常省略......

通過異常我們可以看到,系統在初始化userFeignClient失敗,原因是findById方法的修飾註解(annotated with)沒有遵循HTTP方法的規則,實際上也就意味着,使用了“feign.Contract.Default()”的feignClient,只識別Feign的註釋,不再識別Spring MVC的HTTP註釋。

那麼Feign的HTTP請求註解是什麼?我們可以在Feign的Github(https://github.com/OpenFeign/feign)的README.md中看到類型的註釋:

這裏@RequestLine註解就是Feign的HTTP請求修飾註解,該註解以Request-Line的方式去解析一個HTTP請求串,該請求串由三個參數組成,每個參數之間由空格分隔開來,分別代表method請求方式(GET/POST),Request-URI請求路徑,HTTP-Version請求HTTP協議版本(不寫的話,默認爲HTTP/1.1)。
例如下面的格式:

@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

就是GET請求的、路徑爲“/repos/{owner}/{repo}/contributors”的HTTP請求,其中花括號代表動態參數,可以與下面方法中的參數綁定。

我們這裏改造一下之前UserFeignClient中的findById請求以及postUser請求,將之前的方法暫時註釋掉:

package com.microserver.cloud;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;

import com.microserver.cloud.entity.User;
import com.microserver.config.TestFeignConfiguration;

import feign.Param;
import feign.RequestLine;

@FeignClient(name="microserver-provider-user",configuration = TestFeignConfiguration.class )
public interface UserFeignClient {
    
    @RequestLine("GET /findById/{id}")
    public User findById(@Param("id") Long id);
    
    @RequestLine("POST /postUser")
    public String postUser(@RequestBody User user);
    
//    @RequestMapping(value="/findById/{id}", method=RequestMethod.GET)
//    public User findById(@PathVariable("id") Long id);

//    @RequestMapping(value="/postUser", method=RequestMethod.POST)
//    public String postUser(@RequestBody User user);
    
}

可以看到Param以及RequestLine註解所在的包也是Feign的包(import feign.*)。我們重啓movie服務、,可以看到沒有異常了:

然後在eureka啓動的基礎上,再啓動user工程,分別訪問兩個服務,也都是正常的:


證明我們對Feign的自定義配置成功了~

另外使用url請求的例子如下(使用默認的FeignClientsConfiguretion配置):

@FeignClient(name = "user-service", url = "http://localhost:7900/")
public interface UserFeignClient {
    @RequestMapping(value="/findById/{id}", method=RequestMethod.GET)
    public User findById(@Param("id") Long id);
}

有url的時候,name可以自定義,但不可以和其它的重複。

使用Feign請求動態URL的方式詳見:https://my.oschina.net/joryqiao/blog/1925633

三、Feign的其它配置

1、Feign請求/響應壓縮
Feign提供了啓用請求/響應的GZIP壓縮功能,我們可以通過下面的屬性(在properties或yml配置文件)來進行壓縮操作的開啓:
feign.compression.request.enabled =true
feign.compression.response.enabled =true
除了開啓壓縮操作以外,還可以爲Web服務器設置一些壓縮請求參數的設置:
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
如這裏設置了壓縮介質的類型和最小請求閾值長度。

2、Feign日誌記錄
我們可以爲每個創建的Feign客戶端創建一個Logger日誌記錄器,默認情況下,Logger的名稱是用於創建Feign客戶端的接口的完整類名。要注意的是,Feign日誌記錄僅響應DEBUG級別。

例子(application.yml中):

logging.level.project.user.UserClient:DEBUG

這裏就是爲接口路徑爲“project.user.UserClient”的Feign客戶端設置日誌級別。
另外,我們同時還可以在配置類中覆蓋“logging.Level”參數,爲每個客戶端配置Feign日誌的記錄量,一共有以下選擇:
NONE,沒有記錄(DEFAULT)。
BASIC,僅記錄請求方法和URL以及響應狀態代碼和執行時間。
HEADERS,記錄基本信息以及請求和響應標頭。
FULL,記錄請求和響應的標題,正文和元數據。
例如下面的配置就是將“logging.Level”參數配置爲FULL:

@Configuration
public class FooConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

下面我們來測試一下,在movie工程中,我們首先在application.yml中爲UserFeignClient開啓日誌開關:

logging:
  level:
    com.microserver.cloud.UserFeignClient: DEBUG

然後在TestFeignConfiguration配置文件中設置查看的記錄量爲“FULL”全量:

package com.microserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Contract;
import feign.Logger;

@Configuration
public class TestFeignConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

重啓movie服務(Eureka服務和user服務也是啓動中),訪問之前的http://localhost:7901/movie/1服務,可以在控制檯中看到如下信息:

可以看到日誌中記錄了請求URL參數,響應信息的HTTP的響應碼(200),content-type內容類型、transfer-encoding轉換編碼、消息體等信息,所以我們配置的Feign日誌開關是成功的。

至此,有關Feign組件的介紹就到這裏啦~

本部分參考源碼下載:https://download.csdn.net/download/u013517797/11485067

後面我們來學習Eureka的高級應用部分。

 

參考:《51CTO學院Spring Cloud高級視頻》

轉載請註明出處:https://blog.csdn.net/acmman/article/details/98632461

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章