轉自:https://blog.csdn.net/pengjunlee/article/details/86615408
Feign簡介
Feign是一個聲明式的Web Service客戶端,它能夠讓Web Service客戶端的編寫變得更加容易(你只需創建一個接口,並在接口上添加相應注解即可)。除了Feign自帶的注解外它還支持JAX-RS注解,SpringCloud又為Feign增加了對SpringMVC注解的支持,同時為了能夠使用和Spring Web中默認使用的相同的httpMessageConverter,SpringCloud集成了Ribbon和Eureka,用來在使用Feign時能夠為其提供一個負載均衡的HTTP客戶端。
總起來說,Feign具有如下特性:
1.可插拔的注解支持,包括Feign注解和JAX-RS注解; 2.支持可插拔的HTTP編碼器和解碼器; 3.支持Hystrix和它的Fallback; 4.支持Ribbon的負載均衡; 5.支持HTTP請求和響應的壓縮。
接下來我們將通過對上一章《客戶端負載均衡(Ribbon)》中的 message-center 項目進行改造,演示如何使用Feign。
message-center改造
引入Feign依賴
由於Feign依賴中默認包含了Ribbon,所以只需要在 pom.xml 文件中引入Feign依賴即可,Ribbon依賴無需重復引入:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<properties>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka-Client 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Feign 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- SpringCloud 版本控制依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
修改啟動類
在MessageCenterApplication啟動類上增加@EnableFeignClients注解:
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class MessageCenterApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MessageCenterApplication.class).web(WebApplicationType.SERVLET).run(args);
}
}
這里我們在啟動類中增加了@EnableFeignClients注解,用來開啟Feign客戶端發現功能。
如果你的Feign客戶端類文件不在Spring的包掃描路徑之中,可以在@EnableFeignClients注解中對Feign客戶端的包路徑進行指定。
@SpringBootApplication
@EnableFeignClients(basePackages = "com.pengjunlee.client.**")
public class MessageCenterApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MessageCenterApplication.class).web(WebApplicationType.SERVLET).run(args);
}
}
創建Feign客戶端
在上一章《客戶端負載均衡(Ribbon)》中,對外提供服務的HTTP接口定義在MessageController中。
@RestController
@RequestMapping("/api/v1/msg")
public class MessageController {
@Value("${server.port}")
private String port;
/**
* 返回一條消息
*/
@GetMapping("/get")
public String getMsg() {
return "This message is sent from port: " + port;
}
}
接下來,我們在消費端message-center中為它創建一個Feign客戶端。新建一個接口取名MessageServiceClient,並在上面添加@FeignClient注解,完整代碼如下:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "message-service")
public interface MessageServiceClient {
@GetMapping("/api/v1/msg/get")
public String getMsg();
}
說明:此處@FeignClient注解的name屬性應與message-service應用的spring.application.name屬性相同,表示為message-service服務創建一個Feign客戶端。接口的映射地址路徑以及接口入參都必須與MessageController中的方法完全相同。
調用Feign客戶端
接下來,我們來看一看如何在消費端使用創建好的Feign客戶端對message-service服務進行調用,示例代碼如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.pengjunlee.service.MessageServiceClient;
@RestController
@RequestMapping("/api/v1/center")
public class MessageCenterController {
@Autowired
private MessageServiceClient messageService;
@GetMapping("/msg/get")
public Object getMsg() {
String msg = messageService.getMsg();
return msg;
}
}
啟動應用,再次請求 http://localhost:8781/api/v1/center/msg/get ,返回如下結果表明服務調用成功:

關於傳參
Feign除了支持自帶的注解和JAX-RS注解外,還支持 SpringMVC注解,常用的有:@RequestParam 、@PathVariable、@RequestBody 等。
例如,服務端提供如下兩個服務接口:
/**
* 獲取消息詳情
*/
@GetMapping("/api/v1/msg/detail/{id}")
public MessageEntity getDetail(@PathVariable(name = "id") Long id) {
return messageService.getById(id);
}
/**
* 新建一條消息
*/
@PostMapping("/api/v1/msg/save")
public MessageEntity save(@RequestBody MessageEntity message) {
return messageService.save(message);
}
相應的,在Feign客戶端中可以進行如下定義:
/**
* 獲取消息詳情
*/
@GetMapping("/api/v1/msg/detail/{id}")
public MessageEntity getDetail(@PathVariable(name = "id") Long id) ;
/**
* 新建一條消息
*/
@PostMapping("/api/v1/msg/save")
public MessageEntity save(@RequestBody MessageEntity message) ;
重寫Feign的默認配置
在Spring Cloud對Feign的支持實現中,一個核心的概念就是客戶端命名,每一個Feign客戶端都是整個組件系統的一部分,它們相互協同一起工作來按照需求與遠程服務器取得聯系。並且它們每一個都有自己的名字,應用程序開發人員可以使用@feignclient來給它取名。Spring Cloud按照自己的需要又使用FeignClientsConfiguration為每一個已命名的客戶端創建了一個ApplicationContext,額外包含了一個feign.Decoder、一個 feign.Encoder 和一個 feign.Contract。你可以通過指定@FeignClient注解的contextId 屬性來設置ApplicationContext的名字。
SpringCloud還允許你在FeignClientsConfiguration的基礎之上使用@FeignClient聲明一些額外的配置,從而實現對Feign客戶端的完全控制,如下例所示:
@FeignClient(name = "message-service", configuration = MessageConfiguration.class)
public interface MessageServiceClient {
//..
}
在這個例子中,這個Feign客戶端將由FeignClientsConfiguration 和MessageConfiguration中的組件一起組成(后者會覆蓋前者的配置)。
注意:本例中,MessageConfiguration不必用@Configuration注解進行標注,如果確實要加上@Configuration注解,你需要注意把MessageConfiguration排除在@ComponentScan和@SpringBootApplication掃描的包路徑之外,否則它將成為feign.Decoder、feign.Encoder 和 feign.Contract 等的默認值。
下表列出了 Spring Cloud Netflix 缺省為Feign提供的所有 Bean(Bean類型 Bean名稱:Bean實現):
Decoder feignDecoder: ResponseEntityDecoder (包裝了一個 SpringDecoder) Encoder feignEncoder: SpringEncoder Logger feignLogger: Slf4jLogger Contract feignContract: SpringMvcContract Feign.Builder feignBuilder: HystrixFeign.Builder Client feignClient: 啟用 Ribbon 時是 LoadBalancerFeignClient,否則使用 feign.Client.Default。
你可以使用 OkHttpClient 或者 ApacheHttpClient 的Feign客戶端,只需要將 feign.okhttp.enabled 或者 feign.httpclient.enabled 設置為 true ,並將相應類添加到項目的CLASSPATH即可。你也可以使用自定義的HTTP 客戶端,使用 Apache 時提供一個ClosableHttpClient 類型Bean或者使用OK HTTP時提供一個OkHttpClient 類型Bean。
默認情況下,Spring Cloud Netflix 並沒有為Feign提供下列的Bean,但依然會從Spring容器中查找這些類型的Bean用來創建Feign客戶端。
Logger.Level Retryer ErrorDecoder Request.Options Collection<RequestInterceptor> SetterFactory
創建這些類型的一個Bean 並將它寫到 @FeignClient 聲明的配置中,這樣你就能夠對這些Bean中的每一個進行重寫。例如下面的 MessageConfiguration 利用feign.Contract.Default替代了SpringMvcContract 並向RequestInterceptor 集合中添加了一個RequestInterceptor 。
@Configuration
public class MessageConfiguration {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
}
當然,@FeignClient 也支持通過配置文件進行配置。
feign:
client:
config:
message-service:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.pengjunlee.SimpleErrorDecoder
retryer: com.pengjunlee.SimpleRetryer
requestInterceptors:
- com.pengjunlee.FooRequestInterceptor
- com.pengjunlee.BarRequestInterceptor
decode404: false
encoder: com.pengjunlee.SimpleEncoder
decoder: com.pengjunlee.SimpleDecoder
contract: com.pengjunlee.SimpleContract
默認配置可以通過@EnableFeignClients的defaultConfiguration屬性進行指定,然后會被應用到所有的Feign客戶端。如果你希望使用配置文件對所有的@FeignClient進行配置,可以使用 default 作為Feign客戶端的名稱。
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
如果我們同時使用了@Configuration Bean和文件配置,文件配置會優先生效。如果你希望優先使用 @Configuration Bean中的配置,可以將 feign.client.default-to-properties 設置為 false 。
如果你需要在RequestInterceptor中使用ThreadLocal 變量,你需要將Hystrix 的線程隔離策略設置為 SEMAPHORE 或者直接禁用Hystrix 。
# To disable Hystrix in Feign
feign:
hystrix:
enabled: false
# To set thread isolation to SEMAPHORE
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
關於超時
在啟用Ribbon的情況下,Feign客戶端是一個LoadBalancerFeignClient Bean,其內部有一個 execute() 方法用來發送一個HTTP請求並獲取響應, 本質上其實還是使用的Ribbon做負載均衡,並使用RestTemplate發送的請求。execute() 接口聲明如下:
public Response execute(Request request, Request.Options options) throws IOException;
其中,Request 用來封裝HTTP請求的詳細信息。
/**
*
* An immutable request to an http server.
*
*/
public final class Request {
private final String method;
private final String url;
private final Map<String, Collection<String>> headers;
private final byte[] body;
private final Charset charset;
// ...
}
Options 則封裝了一些請求控制參數:
public static class Options {
private final int connectTimeoutMillis;
private final int readTimeoutMillis;
private final boolean followRedirects;
public Options(int connectTimeoutMillis, int readTimeoutMillis) {
this(connectTimeoutMillis, readTimeoutMillis, true);
}
public Options() {
this(10 * 1000, 60 * 1000);
}
// ...
}
從Options 源碼來看,Feign客戶端默認的讀取超時時間為60秒。若同時使用了Hystrix,由於Hystrix 默認的讀取超時時間為1秒,會導致Feign客戶端默認的讀取超時時間設置無效,即超過1秒即為讀取超時。可使用如下配置同時對Feign客戶端和Hystrix 的超時配置進行重寫。
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
參考文章
https://cloud.spring.io/spring-cloud-openfeign/single/spring-cloud-openfeign.html
https://www.jianshu.com/p/a0d50385e598
https://blog.csdn.net/chengqiuming/article/details/80713471
