Spring Cloud Feign

Feign 是一种声明式、模板化的 HTTP 客户端。在 Spring Cloud 中使用 Feign,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。

Feign 的特性

  • 集成 Ribbon 的负载均衡功能
  • 集成了 Hystrix 的熔断器功能
  • 支持请求压缩
  • 大大简化了远程调用的代码,同时功能还增强啦
  • Feign 以更加优雅的方式编写远程调用代码,并简化重复代码

Spring Cloud Feign 作用

Feign可以把HTTP 的请求进行隐藏,伪装成类似 SpringMVCController一样。你不用再自己拼接 url,拼接参数等等操作,一切都交给 Feign 去做。

创建好了用户,订单,商品微服务,这三个微服务是互相隔离的,那么微服务和微服务之间如何互相调用呢,显然三个微服务都可以采用 http 通信,也就是 restTemplate 进行互相访问,但是这种方式对参数传递和使用都不是很方便,所以弃用此方式,采用 feign 进行服务之间的调用,可以简化调用流程,真正感觉到是在同一个项目中调用另一个类的方法

当我们通过RestTemplate调用其它服务的 API 时,所需要的参数须在请求的 URL 中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下。

在开发 Spring Cloud 微服务的时候,我们知道,服务之间都是以 HTTP 接口的形式对外提供服务的,因此消费者在进行调用的时候,底层就是通过 HTTP Client 的这种方式进行访问。Spring 的 RestTemplate 去实现服务间的调用。但是最方便、最优雅的方式是通过 Spring Cloud Feign 进行服务间的调用。使用 Feign 只需要创建一个接口加上对应的注解,比如:@FeignClient 注解。

快速上手

使用 Feign 替代 RestTemplate 发送 HTTP请求。

1、导入依赖

user-consumer中添加spring-cloud-starter-openfeign依赖

1
2
3
4
5
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、创建 Feign 客户端

user-consumer中创建com.atguigu.feign.UserClient接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.atguigu.feign;

import com.atguigu.domain.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

// value = "要调用微服务的名字"
@FeignClient(value = "user-provider")
public interface UserClient {
/***
* 根据ID查询用户信息
* @param id
* @return
*/
@RequestMapping(value = "/user/find/{id}")
User findById(@PathVariable(value = "id") Integer id);
}

Feign 会通过动态代理,帮我们生成实现类。
注解@FeignClient声明 Feign 的客户端,注解 value 指明服务名称
接口定义的方法,采用 SpringMVC 的注解。Feign 会根据注解帮我们生成 URL 地址
注解@RequestMapping 中的/user,不要忘记。因为 Feign 需要拼接可访问地址

3、编写控制层

user-consumer中创建com.atguigu.controller.ConsumerFeignController,在 Controller 中使用@Autowired注入FeignClient

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping(value = "/feign")
public class ConsumerFeignController {

@Autowired
private UserClient userClient;

@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id") Integer id) {
return userClient.findById(id);
}
}

4、开启 Feign

修改user-consumer的启动类,在启动类上添加@EnableFeignClients注解,开启 Feign

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
// 开启Feign
@EnableFeignClients(basePackages = "com.atguigu.feign")
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}

/***
* 将RestTemplate的实例放到Spring容器中
* @return
*/
@Bean
// 开启负载均衡
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

浏览器请求:http://localhost:18082/feign/2

5、负载均衡

Feign 自身已经集成了 Ribbon,因此使用 Feign 的时候,不需要额外引入依赖

Feign 内置的 ribbon 默认设置了请求超时时长,默认是 1000ms,可以修改

ribbon 内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试可以关闭配置:

user-consumer 配置文件添加如下配置

1
2
3
4
5
6
7
8
9
10
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
ConnectTimeout: 1000 # 连接超时时间 没有连接上
ReadTimeout: 2000 # 数据读取超时时间 连接上了,连上之后开始计时,但读取数据需要花费很长时间
MaxAutoRetries: 1 # 最大重试次数(第一个服务)在第一次连接超时之后,在重新连接一次
MaxAutoRetriesNextServer: 0 # 最大重试下一个服务次数(集群的情况才会用到)
OkToRetryOnAllOperations: false # 是否所有操作都进行重试

6、熔断器支持

Feign 默认也有对 Hystrix 的集成

实现步骤:

  1. 在配置文件 application.yml 中开启 feign 熔断器支持
  2. 编写 FallBack 处理类,实现 FeignClient 客户端
  3. 在@FeignClient 注解中,指定 FallBack 处理类

① 开启 Hystrix

user-consumer 配置文件 application.yml 中开启 feign 熔断器支持:默认关闭

1
2
3
feign:
hystrix:
enabled: true # 开启Feign的熔断功能

② 熔断降级类创建

修改user-consumer,创建一个类com.atguigu.feign.fallback.UserClientFallback,实现刚才编写的 UserClient,作为 FallBack 的处理类

1
2
3
4
5
6
7
8
9
@Component
public class UserClientFallback implements UserClient {
@Override
public User findById(Integer id) {
User user = new User();
user.setUsername("服务熔断");
return user;
}
}

③ 指定 Fallback 处理类

在@FeignClient 注解中,指定 FallBack 处理类

④ 测试

关闭服务消费方,浏览器请求:http://localhost:18082/feign/2

⑤ 请求压缩

SpringCloudFeign 支持对请求和响应进行 GZIP 压缩,以减少通信过程中的性能损耗。

user-consumer通过配置开启请求与响应的压缩功能:(简单压缩)

1
2
3
4
5
6
7
8
9
feign:
hystrix:
enabled: true # 开启Feign的熔断功能

compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩

也可以对请求的数据类型,以及触发压缩的大小下限进行设置(完整压缩)

1
2
3
4
5
6
7
8
9
10
11
12
feign:
hystrix:
enabled: true # 开启Feign的熔断功能

compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
#以上数据类型,压缩大小下限均为默认值
response:
enabled: true # 开启响应压缩

⑥Feign 的日志级别配置

一般都是通过 loggin.level.xx=debug 来设置日志级别。然而这个对 Feign 客户端不会产生效果。因为@FeignClient 注解修饰的客户端在被代理时,都会创建一个新的 Feign.Logger 实例。我们需要额外通过配置类的方式指定这个日志的级别才可以。

实现步骤

  1. 在 application.yml 配置文件中开启日志级别配置
  2. 编写配置类,定义日志级别 bean
  3. 在接口的@FeignClient 中指定配置类
  4. 重启项目,测试访问
[1]普通日志等级配置

user-consumer的配置文件application.yml中设置com.atguigu包下的日志级别都为debug

1
2
3
4
# com.atguigu 包下的日志级别都为Debug
logging:
level:
com.atguigu: debug

[2]Feign 日志等级配置

user-consumer中的 application 启动类com.atguigu.UserConsumerApplication,定义日志级别

1
2
3
4
5
6
7
8
/**
* 配置日志级别
* @return
*/
@Bean
public Logger.Level feignloggerlevel() {
return Logger.Level.FULL;
}

[3]日志级别说明

Feign 支持 4 中级别:
NONE:不记录任何日志,默认值
BASIC:仅记录请求的方法,URL 以及响应状态码和执行时间
HEADERS:在 BASIC 基础上,额外记录了请求和响应的头信息
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据

运行程序:http://localhost:18082/feign/2

即可看到每次访问的日志, 文件压缩

⑦Feign 工作原理

① 在开发微服务应用时,我们会在主程序入口添加 @EnableFeignClients 配置的扫描包路径。如果没配置,默认为启动类的包路径。

② 当程序启动时,会进行包扫描,扫描所有 @FeignClient 的注解的类,并将这些信息注入 Spring IOC 容器中。

③ 校验 @FeignClient 修饰的类,包括类必须是 interface ,以及@FeignClientfallbackfallbackFactory配置的必须是接口的实现类。

7、小结

  • Feign 的作用:不再使用拼接 URL 的方式实现远程调用,以接口调用的方式实现远程调用,简化了远程调用的实现方式,增强了远程调用的功能,例如:增加了负载均衡、熔断、压缩、日志启用。

  • Feign 的使用过程:引入 Feign 依赖包 ———> 创建 Feign 接口,feign 接口中需要指定调用的服务名字 ———> 使用@EnabledFeignClients启用 Feign 功能

  • Feign 的负载均衡配置:在配置文件中配置{spring.application.name}:ribbon: ​ 负载均衡属性配置

1
2
3
4
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
  • Feign 的熔断配置:在 application.yml 中开启 Hystrix ———> 给 Feign 接口创建一个实现类 ———> 给 Feign 指定 fallback 类
  • Feign 的压缩配置:在 application.yml 中指定压缩属性即可
  • Feign 的日志配置:在 application.yml 中开启普通日志等级 ———> 创建一个类 ———> 定义 Feign 日志等级,在 Feign 接口中指定定义日志的配置