1. 程式人生 > 實用技巧 >Feign的介紹和使用

Feign的介紹和使用

一、Feign的介紹

  Feign是一個宣告式 WebService 客戶端,使用Feign能夠讓編寫Web Service 客戶端更加簡單,它的使用方法是定義一個介面,然後在上面添加註解,同時也支援JAX-RS標準的註解。Feign也支援可插拔式的編碼器和解碼器。

  Spring Cloud 對 Fiegn 進行了封裝,使其支援了Spring MVC 標準註解和HttpMessageConverts。Feign可以與Eureka和Ribbon組合使用以支援負載均衡。

  前面使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的呼叫方法,但是在實際的開發中,由於對服務依賴的呼叫可能不止一處。往往一個介面會被多處呼叫,所以通常會對每個微服務自行封裝一些客戶端類來包裝依賴服務的呼叫,所以Feign在此基礎上做了進一步封裝,有他來幫助我們自定義和實現依賴服務介面的定義,在Feign的實現下,我們只需要建立一個介面並使用註解的方式來配置他(以前是Dao介面上面標註Mapper註解,現在是一個微服務介面上面標註一個Feign註解即可),即可完成服務提供放的介面繫結,簡化了使用Spring Cloud Ribbon時,自動封裝服務呼叫客戶端的開發量。

二、Feign的使用

通常我們會把 feign 的定義作為一個單獨的工程,以供其他服務呼叫;

1、feign的專案

(1)依賴 feign 的

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

(2)修改打包方式

<build>
    <plugins>
	<plugin>
	        <groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-jar-plugin</artifactId>
	</plugin>
    </plugins>
</build>

(3)編寫宣告式介面

@FeignClient("product-center")
public interface ProductCenterFeignApi {

    /**
     * 宣告式介面,遠端呼叫http://product-center/v1/person/{id}
     * @param id
     * @return
     */
    @RequestMapping("/v1/person/{id}")
    Person getPerson(@PathVariable("id") String id);
}

2、呼叫 feign 的專案

order-center 服務呼叫 product-center 服務;

(1)引入上面定義的 feign 專案的依賴

 <dependency>
        <groupId>com.yufeng</groupId>
        <artifactId>cloud-feign-api</artifactId>
        <version>1.0-SNAPSHOT</version>
</dependency>

(2)啟動類增加@EnableFeignClients

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

(3)order-center 服務呼叫 product-center(像呼叫本地方式一樣呼叫遠端服務

@RestController
public class OrderController {

    @Autowired
    private ProductCenterFeignApi productCenterFeignApi;
    
    @GetMapping("/v1/getProduct/{id}")
    public String getProduct(@PathVariable("id") String id) {
        Person person = productCenterFeignApi.getPerson(id);
        return person.toString();
    }
}

3、Feign的自定義配置

在預設情況下,Feign的呼叫是不列印日誌;需要我們通過自定義來列印 feign 日誌。

(1)feign 日誌的級別定義

  • NONE(預設):不記錄任何日誌;
  • BASIC: 僅列印請求方法、URL、響應狀態碼、執行時間;(生產環境推薦
  • HEADERS: 記錄 BASIC 級別的基礎上,記錄請求和響應的 header;
  • FULL: 記錄請求和響應的 header、body和元資料;

(2)feign日誌的定義

方法一:配置類的方式,並指定 @FeignClient 註解的 configuration 屬性

<1> 定義一個 feign 的配置類(注意:不可以被包掃描到(不可加@Configuration註解),否則會作為全域性配置

public class ProductCenterFeignConfig {
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }
}

<2> 將定義的 feign 配置類配置到對應的位置,如下:

package com.yufeng.feignapi;

@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {

    /**
     * 宣告式介面,遠端呼叫http://product-center//v1/person/{id}
     * @param id
     * @return
     */
    @RequestMapping("/v1/person/{id}")
    Person getPerson(@PathVariable("id") String id);
}

<3> 呼叫端工程 order-center 服務,將 com.yufeng.feignapi包下日誌級別定義為 debug,否則是不會列印 feign 的日誌的。

logging:
  level:
    com:
      yufeng:
        feignapi: debug

方法二:配置檔案的方式

不用指定 @FeignCilent 註解的 configuration 的選項

# com.yufeng.feignapi 包的日誌級別定義為 debug;
logging:
  level:
    com:
      yufeng:
        feignapi: debug

# feign的日誌級別定義為FULL
feign:
  client:
    config:
      product-center:
        loggerLevel: FULL

列印的日誌結果:

4、feign的使用其原生註解配置

我們上面的例子中,feign 的配置使用的 SpringMVC的註解 @RequestMapping;因為自動裝配的 FeignCilents 配置中的預設契約是 SpringMVC;如果我們要使用 feign 的原生註解,首先要修改契約的配置。

方式一:使用註解的方式配置

(1)修改feign的契約,在feign的定義 ProductCenterFeignConfig 中增加配置

public class ProductCenterFeignConfig {
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }

    /**
     * 通過修改契約為預設的Feign的鍥約,那麼就可以使用預設的註解
     * @return
     */
    @Bean
    public Contract feignContract() {
        return new Contract.Default();
    }
}

(2)將上面定義的配置類,指定給 @FeignClient 的 Configuration 選項。

@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
//@FeignClient(value = "product-center")
public interface ProductCenterFeignApi {

    /**
     * 修改鍥約為Feign的  那麼就可以使用預設的註解
     * @param id
     * @return
     */
    @RequestLine("GET /v1/person/{id}")
    Person getPerson(@Param("id") String id);
}

方式二:使用配置檔案的方式配置

(1)修改預設契約

feign:
  client:
    config:
      product-center:
        contract: feign.Contract.Default

(2)修改為 feign 的原生註解

@FeignClient(value = "product-center")
public interface ProductCenterFeignApi {

    /**
     * 修改鍥約為Feign的  那麼就可以使用預設的註解
     * @param id
     * @return
     */
    @RequestLine("GET /v1/person/{id}")
    Person getPerson(@Param("id") String id);
}

因為feign的註解我們不常用,推薦使用 Spring MVC 的註解

三、Feign的高階使用

1、服務呼叫的請求頭透傳

我們使用 PostMan 去呼叫 product-center 服務攜帶了 header,product-center的介面又去呼叫了 order-center 服務,需要把請求頭傳遞給 order-center 服務的。

(1)自定義一個攔截器實現 RequestInterceptor 介面

public class CustomRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(null != requestAttributes) {
            HttpServletRequest request = requestAttributes.getRequest();
            requestTemplate.header("Token", request.getHeader("token"));
        }
    }
}

(2)將這個攔截器給配置到feign的配置上, @FeignClient 註解的 configuration 選項上;

public class ProductCenterFeignConfig {
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return new CustomRequestInterceptor();
    }
}
@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {

    /**
     * 宣告式介面,遠端呼叫http://product-center//v1/person/{id}
     * @param id
     * @return
     */
    @RequestMapping("/v1/person/{id}")
    Person getPerson(@PathVariable("id") String id);
}

2、feign的呼叫優化方案

開啟連線池配置,使feign的底層呼叫使用 HttpClient,這樣就有連線池的概念;

feign:
  client:
    config:
      product-center:
        loggerLevel: BASIC
  httpclient:  # feign的底層去呼叫HttpClient
    enabled: true
    max-connections: 200  #最大連線數
    max-connections-per-route: 50   #為每個url請求設定最大連線數