五、使用Feign实现声明式REST调用

Feign 简介

Feign 是Netflix 开发的声明式、模块化的HTTP客户端,Feign 可帮助我们更加便捷、优雅地调用HTTP API。在Spring Cloud 中,使用Feign 非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign 支持多种注解,例如Feign 自带的注解或者JAX-RS注解等。Spring Cloud 对 Feign进行了增强,使Feign 支持了 Spring MVC 注解, 并整合了Ribbon 和 Eureka,从而让Feign 的使用更加方便。

为服务消费者整合Feign

添加Feign 的依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

创建Feign接口,并添加@FeignClient 注解

1
2
3
4
5
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}

@FeignClient 注解中的 microservice-provider-user 是一个任意客户端的名称,用于创建Ribbon 负载均衡器。

修改Controller 代码,让其调用 Feign 接口

1
2
3
4
5
6
7
8
9
10
@RestController
public class MovieController {
@Autowired
private UserFeignClient userFeignClient;

@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
return this.userFeignClient.findById(id);
}
}

修改启动类,为其添加@EnableFeignClients注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class ConsumerMovieApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieApplication.class, args);
}
}

自定义 Feign 配置

使用 Java 代码自定义 Feign 配置

  1. 创建 Feign 的配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 该类为Feign的配置类
* 注意:该不应该在主应用程序上下文的@ComponentScan中。
* @author 周立
*/
@Configuration
public class FeignConfiguration {
/**
* 将契约改为feign原生的默认契约。这样就可以使用feign自带的注解了。
* @return 默认的feign契约
*/
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
  1. Feign 接口 通过@FeignClient 的 configuration 属性指定配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 使用@FeignClient的configuration属性,指定feign的配置类。
* @author 周立
*/
@FeignClient(name = "microservice-provider-user", configuration = FeignConfiguration.class)
public interface UserFeignClient {
/**
* 使用feign自带的注解@RequestLine
* @see https://github.com/OpenFeign/feign
* @param id 用户id
* @return 用户信息
*/
@RequestLine("GET /{id}")
public User findById(@Param("id") Long id);
}

类似的,还可以自定义 Feign 的编码器、解码器、日志打印、甚至为 Feign 添加拦截器。

在 Spring Cloud Edgware 中,Feign 的配置类无须添加@Configuration 注解,如果加了@Configuration 注解,那么该类不能放到@ComponentScan 所扫描的包下。否则,该类中的所有配置就会被所有的 @FeignClient 共享。
全局配置:
注解 @EnableFeignClients 为我们提供了defaultConfiguration 属性,用来指定默认的配置类。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

使用属性自定义 Feign 配置

从 Spring Cloud Edgware 开始,Feign 支持使用属性自定义配置。

对于指定名称的 Feign Client,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
feign:
client:
config:
feignName:
# 相当于 Request.Options
connectTimeout: 5000
# 相当于 Request.Options
readTimeout: 5000
#配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: basic
# Feign 的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
# 配置拦截器,相当于代码配置方式中的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
通用配置:
feign:
client:
config:
default:
# 相当于 Request.Options
connectTimeout: 5000
# 相当于 Request.Options
readTimeout: 5000
#配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: basic
# Feign 的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
# 配置拦截器,相当于代码配置方式中的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false

属性配置的方式比Java 代码配置的方式优先级高。如果你想让Java 代码配置方式优先级更高,可使用这个属性: feign.client.default-to-properties=false。

Feign 对压缩的支持

在一些场景下,可能需要对请求或响应进行压缩,此时可使用以下属性启用Feign 的压缩功能

1
2
3
4
5
6
7
8
feign:
compression:
request:
enabled: true #启用请求压缩
mime-types: text/xml,application/xml,application/json #用于支持的媒体类型列表,默认
min-request-size: 2048 #用于设置请求的最小阈值,默认
response:
enabled: true #启用响应压缩

Feign 的日志

Feign 对日志处理非常灵活,可为每个Feign 客户端指定日志记录策略,每个 Feign 客户端都会创建一个 logger。默认情况下,logger 的名称是Feign 接口的完整类名。需要注意的是,Feign 的日志打印只会对DEBUG 级别做出响应。

我们可为每个Feign 客户端配置各自的Logger.Level 对象,告诉Feign 记录哪些日志。 Logger.Level 的值有以下选择。
NONE: 不记录任何日志(默认值)
BASIC: 仅记录请求方法、URL、响应状态吗以及执行时间
HEADERS:记录BASIC 级别的基础上,记录请求和响应的header 。
FULL: 记录请求和响应的 header 、body 和元数据。
编码方式设置日志级别:

  1. 编写 Feign 配置类
1
2
3
4
5
6
7
@Configuration
public class FeignLogConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
  1. 修改 Feign 的接口,指定配置类:
1
2
3
4
5
@FeignClient(name = "microservice-provider-user", configuration = FeignLogConfiguration.class)
public interface UserFeignClient {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
  1. 配置application.yml,指定 Feign 接口的日志级别为 DEBUG
1
2
3
logging:
level:
com.itmuch.cloud.study.user.feign.UserFeignClient: DEBUG # 将Feign接口的日志级别设置成DEBUG,因为Feign的Logger.Level只对DEBUG作出响应。

使用属性配置日志级别
从Spring Cloud Edware 开始,也可以使用配置属性直接定义 Feign 的日志级别

1
2
3
4
5
6
7
8
feign:
client:
config:
microservice-provider-user:
loggerLebel: full
logging:
level:
com.itmuch.cloud.study.user.feign.UserFeignClient: DEBUG # 将Feign接口的日志级别设置成DEBUG,因为Feign的Logger.Level只对DEBUG作出响应。

使用 Feign 上传文件

为应用 添加 feign-form 相关依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.8.0</version>
</dependency>

编写FeignConfiguration

1
2
3
4
5
6
public class FeignConfiguration {
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}

编写 FeignClient

1
2
3
4
5
@FeignClient(name = "microservice-file-upload", configuration = FeignConfiguration.class)
public interface UploadFeignClient {
@RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
}