1. 程式人生 > >Spring Cloud微服務解決方案④:Feign的使用

Spring Cloud微服務解決方案④:Feign的使用

Feign是一個宣告web服務客戶端,這便得編寫web服務客戶端更容易,使用Feign 建立一個介面並對它進行註解,它具有可插拔的註解支援包括Feign註解與JAX-RS註解,Feign還支援可插拔的編碼器與解碼器,Spring Cloud 增加了對 Spring MVC的註解,Spring Web 預設使用了HttpMessageConverters, Spring Cloud 整合 Ribbon 和 Eureka 提供的負載均衡的HTTP客戶端 Feign.

demo下載地址:https://download.csdn.net/download/qq_22075041/10851487在microservice-consumer-movie-feign模組裡面。

首先我們需要在pom上加Feign的依賴

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

在啟動類上加一個註解@EnableFeignClients申明一下

然後就是關於服務呼叫了 如下:

package com.itmuch.cloud.feign;

import org.springframework.cloud.netflix.feign.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.itmuch.cloud.entity.User;
//加一個註解,"microservice-provider-user"是服務的虛擬IP
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
  public User findById(@PathVariable("id") Long id); // 兩個坑:1. @GetMapping不支援   2. @PathVariable得設定value

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

  // 該請求不會成功,只要引數是複雜物件,即使指定了是GET方法,feign依然會以POST方法進行傳送請求。可能是我沒找到相應的註解或使用方法錯誤。
  @RequestMapping(value = "/get-user", method = RequestMethod.GET)
  public User getUser(User user);
}

以上就是一個feign的簡單使用。注意他有幾個坑點。

下面我要自定義feign配置 怎麼做呢,原始碼對應microservice-consumer-movie-feign-customizing模組。

package com.itmuch.config;

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

import feign.Contract;
import feign.Logger;

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

  @Bean
  Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;//控制檯會輸出debug日誌
  }
}

以上的程式碼 我們自定義了feign的Contract實現為fengin的預設實現,還有日誌級別(還需要在配置檔案中加如下一行程式碼)

//com.itmuch.cloud.feign.UserFeignClient類的日誌為debug
logging: level: com.itmuch.cloud.feign.UserFeignClient: DEBUG

注意上一篇文章講到的警告@Configuration註解不能放在@ComponentScan、@SpringBootApplication包以及子包下得問題,此處依然會存在,解決方案請看上一篇。

然後在UserFeignClient類上面的@FeignClient註解加一個屬性 configuration = Configuration1.class。

package com.itmuch.cloud.feign;

import org.springframework.cloud.netflix.feign.FeignClient;

import com.itmuch.cloud.entity.User;
import com.itmuch.config.Configuration1;

import feign.Param;
import feign.RequestLine;

@FeignClient(name = "microservice-provider-user", configuration = Configuration1.class)
public interface UserFeignClient {
  //Configuration1類定義了feign的預設實現,他預設實現就是這麼寫的 下面倆行程式碼
  @RequestLine("GET /simple/{id}")
  public User findById(@Param("id") Long id);
}

以上就實現了feign的自定義配置。官方說的@FeignClient就是一個子容器(就像是Spring和SpringMVC容器的關係),當然裡面還還包含很多東西,感興趣自行研究。

但是有時候UserFeignClient類是這樣定義的,但是我UserFeignClient2、3、4的自定義配置不想用feign的剛才的配置啊,我又寫了一個Configuration2。加入了basic認證的bean。

package com.itmuch.config;

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

import feign.auth.BasicAuthRequestInterceptor;

@Configuration
public class Configuration2 {
  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor("user", "password123");
  }
}

然後看我的FeignClient2這樣寫:

package com.itmuch.cloud.feign;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.itmuch.config.Configuration2;

@FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class)
//xxxx就相當於http://localhost:8761/的別名
public interface FeignClient2 {
  //注意下面我們使用的Contract實現是springMVC的實現 不再是feign的。
  @RequestMapping(value = "/eureka/apps/{serviceName}")
  public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
}

除了此外feign還支援gzip等等,深入的話請檢視官方文件

題外話:有時候在第一次請求時報超時異常,是因為hystrix,他是一個斷路器,後面的文章會講到,他預設第一次響應時間超過1秒則中斷,那麼解決方案就是在配置檔案裡面延長他的響應時間,或者禁用超時,或者直接禁用hystrix。

# hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
# 或者:
# hystrix.command.default.execution.timeout.enabled: false
# 或者:
feign.hystrix.enabled: false ## 索性禁用feign的hystrix

# 超時的issue:https://github.com/spring-cloud/spring-cloud-netflix/issues/768
# 超時的解決方案: http://stackoverflow.com/questions/27375557/hystrix-command-fails-with-timed-out-and-no-fallback-available
# hystrix配置: https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.thread.timeoutInMilliseconds