Spring Cloud Feign 整合了 Spring Cloud Ribbon 和 Spring Cloud Hystrix 的功能,提供了一种声明式的 Web 服务客户端定义方式。它在 Open Feign 的基础上扩展了对 Spring MVC 的注解支持。
构建 Feign 客户端
1. 创建工程
使用 Spring Initializr 生成一个 Spring Cloud Feign 的 Maven 项目
- Group:com.yjw.springcloudfeignconsumer
- Artifact:spring-cloud-feign-consumer
- Dependencies:
- Spring Boot Actuator
- Spring Web Starter
- OpenFeign
- Ribbon
- Eureka Discovery Client
注意:
- Ribbon 必须要引入,否则控制台会报错。
- Eureka Discovery Client 也必须要引入,否则该服务不会注册到服务注册中心。
2. 添加 @EnableFeignClients 注解
主类 Application 添加 @EnableFeignClients 注解,开启 Spring Cloud Feign 的支持功能。
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 服务调用
定义服务接口,通过 @FeignClient 注解指定服务名来绑定服务,然后再使用 Spring MVC 的注解来绑定具体服务提供的 REST 接口。
/**
* eureka-provider 是服务名,不区分大小写
*/
@FeignClient("eureka-provider")
public interface ProviderClient {
@GetMapping("/user/{id}")
void getUserById(@PathVariable("id") Long id);
@GetMapping("/user/getUserByName")
void getUserByName(@RequestParam("name") String name);
@GetMapping("/user/getUser")
void getUser(@SpringQueryMap UserQuery userQuery);
@PutMapping("/user/update")
void updateUser(UserQuery userQuery);
@PostMapping("/user/add")
void addUser(UserQuery userQuery);
@DeleteMapping("/user/{id}")
void deleteUserById(@PathVariable("id") Long id);
}
注意:这里的服务名不区分大小写。
4. 配置
application.properties 配置内容和服务提供者差不多,只需要修改一下应用名称和端口号。
spring.application.name=feign-consumer
server.port=7001
logging.level.root=info
#打开shutdown
management.endpoint.shutdown.enabled=true
#支持HTTP请求
management.endpoints.web.exposure.include=*
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9000/eureka/,http://localhost:9001/eureka/
5. 测试验证
项目地址:https://gitee.com/yin_jw/demo/tree/master/springcloud-demo/spring-cloud-ribbon-consumer
我们先启动服务注册中心以及两个 eureka-provider 服务,然后启动 feign-consumer 服务,查看 Eureka 信息面板可以看到各个服务的启动情况。
使用 Postman 测试接口,可以在服务提供者控制台看到接口输出。我们可以看到使用 Feign 实现的消费者,依然是利用 Ribbon 维护了针对 eureka-provider 的服务列表信息,并且实现了客户端负载均衡。
参数绑定
Spring Cloud Feign 的参数绑定和 Spring MVC 类似,注解的使用可以参考 Spring MVC 的使用,这里只需要注意一下 /user/getUser 接口的 @SpringQueryMap 注解,该注解为 POJO 提供了支持,可用作 GET 参数映射。
简单的参数绑定使用在 spring-cloud-feign-consumer 项目中有具体的示例,可作为参考。
Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以通过配置 Ribbon 的配置来自定义客户端调用的参数。
#全局配置
ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000
#指定服务配置
eureka-provider.ribbon.ConnectTimeout=500
eureka-provider.ribbon.ReadTimeout=2000
#重试机制
eureka-provider.ribbon.OkToRetryOnAllOperations=false
eureka-provider.ribbon.MaxAutoRetries=0
eureka-provider.ribbon.MaxAutoRetriesNextServer=1
全局配置
使用 ribbon.
ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000
指定服务配置
使用
eureka-provider.ribbon.ConnectTimeout=500
eureka-provider.ribbon.ReadTimeout=2000
重试机制配置
Spring Cloud Feign 默认实现了请求的重试机制,下面的配置可以设置 Feign 的重试机制。
eureka-provider.ribbon.OkToRetryOnAllOperations=false
eureka-provider.ribbon.MaxAutoRetries=0
eureka-provider.ribbon.MaxAutoRetriesNextServer=1
- eureka-provider.ri**bbon.OkToRetryOnAllOperations**:对所有操作请求都进行重试。默认值为 false,只对 GET 请求进行重试。该参数的使用源码参考 RibbonLoadBalancedRetryPolicy#canRetry 方法。
- eureka-provider.ribbon.MaxAutoRetriesNextServer:切换实例的重试次数,不包括首次调用。
- eureka-provider.rib**bon.MaxAutoRetries**:对当前实例的重试次数,不包括首次调用。
注意:Ribbon 的超时与 Hystrix 的超时是两个概念。为了可以实现 Feign 的重试机制,需要让 Hystrix 的超时时间大于 Ribbon 的超时时间,否则 Hystrix 命令超时后,该命令直接熔断,重试机制就没有任何意义了。
Hystrix 配置
全局配置
默认情况下,Spring Cloud Feign 会将所有 Feign 客户端的方法都封装到 Hystrix 命令中进行服务保护。
#全局配置,超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
服务降级配置
- 服务降级逻辑的实现只需要为 Feign 客户端的定义接口编写一个具体的接口实现类。其中每个重写方法的实现逻辑都可以用来定义相应的服务降级逻辑。
/**
* 服务降级
*/
@Component
public class ProviderFallback implements ProviderClient {
@Override
public void getUserById(Long id) {
System.out.println("getUserById 服务降级");
}
@Override
public void getUserByName(String name) {
System.out.println("getUserByName 服务降级");
}
@Override
public void getUser(UserQuery userQuery) {
System.out.println("getUser 服务降级");
}
@Override
public void updateUser(UserQuery userQuery) {
System.out.println("updateUser 服务降级");
}
@Override
public void addUser(UserQuery userQuery) {
System.out.println("addUser 服务降级");
}
@Override
public void deleteUserById(Long id) {
System.out.println("deleteUserById 服务降级");
}
}
- 在服务绑定接口 ProviderClient 中,通过 @FeignClient 注解的 fallback 属性来指定对应的服务降级实现类。
@FeignClient(name ="eureka-provider", fallback = ProviderFallback.class)
public interface ProviderClient {
......
}
日志配置
Spring Cloud Feign 在构建被 @FeignClient 注解修饰的服务客户端时,会为每一个客户端都创建一个 feign.Logger 实例,可以利用该日志对象的 DEBUG 模式来帮助分析 Feign 的请求细节。可以在 application.properties 文件中使用 logger.level.
logging.level.com.yjw.springcloudfeignconsumer.manager.client.ProviderClient=debug
只是添加了如上配置,还无法实现对 DEBUG 日志的输出。这是由于 Feign 客户端默认的 Logger.Level 对象定义为 NONE 级别,该级别不会记录任何 Feign 调用过程中的信息,所以我们需要调整它的级别,针对全局的日志级别,可以在应用主类中直接加入 Logger.Level 的Bean 创建,具体如下:
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
也可以通过实现配置类,然后再具体的 Feign 客户端来指定配置类以实现是否要调整不同的日志级别,比如下面的实现:
@Configuration
public class FullLogConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
@FeignClient(name ="eureka-provider", configuration = FullLogConfiguration.class)
public interface ProviderClient {
......
}
在调整日志界别为 FULL 之后,我们再次访问 http://localhost:7000/user/1 接口,这时我们在 feign-consumer 的控制台中就可以看到如下详细日志:
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] ---> GET http://eureka-provider/user/1 HTTP/1.1
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] ---> END HTTP (0-byte body)
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] <--- HTTP/1.1 200 (16ms)
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] content-length: 0
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] date: Wed, 19 Jun 2019 02:24:48 GMT
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById]
DEBUG 23580 --- [nio-7000-exec-3] c.y.s.manager.client.ProviderClient : [ProviderClient#getUserById] <--- END HTTP (0-byte body)
INFO 23580 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
对于 Feign 的 Logger 级别主要有下面4类,可以根据实际需要进行调整使用。
- NONE:不记录任何信息。
- BASIC:仅记录请求方法、URL 以及相应状态码和执行时间。
- HEADERS:除了记录 BASIC 级别的信息之外,还会记录请求和相应的头信息。
- FULL:记录所有请求与相应的明细,包括头信息、请求体、元数据等。