摘要:Feign 是由 Netflix 开源的声明式的 HTTP 客户端,通过使用定义简单的接口,并声明 Feign 提供的注解,来实现 HTTP 的调用。
目录
[TOC]
Feign
Feign 是由 Netflix 开源的声明式的 HTTP 客户端,目前已经捐献给 OpenFeign 社区。
- 通过使用定义简单的接口,并声明 Feign 提供的注解,来实现 HTTP 的调用。
Spring Cloud OpenFeign
Spring Cloud OpenFeign,将 Feign 集成到 Spring Cloud 体系中,实现服务的声明式 HTTP 调用。
-
相比使用 RestTemplate 实现服务的调用,Feign 简化了代码的编写,提高了代码的可读性,大大提升了开发的效率。
- 可插拔的注解支持,包括
Feign、JAX-RS、SpringMvc注解。- 除了支持 Feign 自带的注解之外,额外提供了对 JAX-RS 注解、SpringMVC 注解的支持。
- 特别是对 SpringMVC 注解的支持,简直是神来之笔,让我们不用学习 Feign 自带的注解,而直接使用超级熟悉的 SpringMVC 注解。
- 同时,Spring Cloud OpenFeign 进一步将 Feign 和 Ribbon 整合(和Eureka?),提供了负载均衡的功能。另外,Feign 自身已经完成和 Hystrix 整合,提供了服务容错的功能。可以不再需要显式地使用这两个组件。
如此,基于注解,极其简单的实现服务的调用,并且具有负载均衡、服务容错的功能。
用法:
- Feign是声明式的服务调用工具,只需创建一个接口、并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用RestTemplate来调用服务接口的开发量。
- 提供了HTTP请求的模板,通过编写简单的接口和注解,就可以定义好HTTP请求的参数、格式、地址等信息。
- Feign会完全代理HTTP请求,开发时只需要像调用方法一样调用它就可以完成服务请求及相关处理。
另外:
- 支持可插拔的HTTP编码器和解码器;
- 支持HTTP请求和响应的压缩。
可设计一套稳定可靠的弹性客户端调用方案,避免整个系统出现雪崩效应。
将Feign视为Spring RestTemplate使用接口与endpoints进行通信。
- 该接口将在运行时自动实现,不是使用服务URL地址,而是使用服务名称。
Feign 与 RestTemplate 的对比
-
从开发效率、可维护性的角度来说,Feign 更加有优势。
-
从执行性能、灵活性的角度来说,RestTemplate 更加有优势。
个人推荐使用 Feign 为主,RestTemplate 为辅:
- 相比来说,开发效率、可维护性非常重要,要保证开发的体验。
- 执行性能的问题,因为 Feign 多一层 JDK 动态代理,所以会差一些。不过 HTTP 调用的整体性能的大头在网络传输和服务端的执行时间,所以 Feign 和 RestTemplate 的性能差距可以相对忽略。
- 灵活性的问题,99.99% 的情况下,Feign 都能够实现或者相对绕的实现;无法实现的情况下,在考虑采用 RestTemplate 进行实现。
Spring Boot 独立集成 Feign
在纯 Spring Boot 环境下,如何使用 Feign 框架。
基本所有的网上文章,都是通过 Spring Cloud Netflix Feign 进行 Feign 的使用,这样就引入了大量 Spring Cloud 的依赖。而我们的希望,可能仅仅只想使用 https://github.com/OpenFeign/feign 核心库。
- 原因是,采用 Spring Boot + Dubbo 实现微服务架构,同时希望使用 Feign 作为 HTTP 客户端调用外部的三方服务,而不再直接使用
HttpClient、RestTemplate等等。
Feign Client
如果没有Feign,
- 将不得不将EurekaClient的一个实例自动连接到控制器中,
- 通过该实例可以接收一个以service-name命名的服务信息、作为Application对象。
- 根据此对象获取(该服务的)所有实例的列表,选择一个合适的实例,然后使用此实例获取主机名和端口。
- 这样,可以对任何http客户端发出标准请求。
在服务器端,可以将它们实现为@Controller,而在客户端,可以将其扩展和注解为@FeignClient。
- Feign客户端:位于spring-cloud-starter-feign软件包中。
- 通过在主程序类上加
@EnableFeignClients注解来启用。 - 要使用它,只需使用
@FeignClient("service-name")注解一个接口,然后将其自动连接到控制器中即可。- 创建此类Feign客户端的一种好方法是使用
@RequestMapping注解方法创建接口,并将其放入单独的模块中。 这样,它们可以在服务器和客户端之间共享。
- 创建此类Feign客户端的一种好方法是使用
- 通过在主程序类上加
api 包:

User.java 放在 api 包中:
- 好处:方便保证服务生产者和消费者使用同一个 UserDTO 实体类。

搭建步骤
搭建一个 Spring Cloud OpenFeign 组件的快速入门示例。步骤如下:
- 首先,搭建一个服务提供者
demo-provider,使用 Spring MVC 注解@RestController提供 RESTful HTTP 接口。- 启动 2 个实例,注册服务到 Nacos 中。用于测试 Ribbon 负载均衡。
- 然后,搭建一个服务消费者
demo-consumer,使用 Ribbon 进行负载均衡,使用 Feign 声明式调用服务提供者demo-provider的 HTTP 接口。
继承特性
Spring Cloud OpenFeign 提供了 SpringMVC 注解的支持,所以可以将服务提供者 Controller 提取出一个接口(即 -api),让服务提供者和消费者共同使用。
这就是 Spring Cloud OpenFeign 提供的继承特性。

结构
-rpc-service包:搭建服务提供者 API 项目:提供服务提供者的 API 接口和 DTO 类。api包:提供服务提供者的 API 接口。dto包:提供数据传输对象 DTO 类。封装复杂参数。
-privider/-rpc-service-impl/-server包:apiImpl包:实现 API 接口。controller包service包
实践建议
在 Spring Cloud OpenFeign 官方文档不推荐使用继承特性:
- 因为通过 Java 接口的共享,导致服务提供者和消费者的耦合,而微服务的目的是为了服务提供者和消费者的解耦,存在一定的冲突。
不过实际场景下,蛮多公司采用继承特性,显而易见的好处,可以方便服务消费者的快速接入,基本无需编写额外的代码。
具体怎么选择,可以自己进行评估,看看使用继承特性的情况下,在享受优点的同时,是否能够接受带来的缺点。
- 个人意见的话,是支持采用继承特性。
- 从 Dubbo 的使用方式来说,也可以认为它是支持采用继承特性。
搭建服务提供者
依赖、配置
将 Nacos 作为注册中心,并实现对其的自动配置。
ProviderController
创建 ProviderController 类,提供 HTTP 接口。
1 | |
DemoProviderApplication
创建 DemoProviderApplication 类,创建应用启动类。
搭建 feign-service 客户端(服务消费者)
这里创建一个feign-service模块来演示feign的常用功能。
使用 Feign 声明式调用服务提供者 demo-provider 的 HTTP 接口。
依赖
主要引入 Spring Cloud Nacos Discovery + Spring Cloud OpenFeign 相关依赖。
- 这里没有主动引入
spring-cloud-netflix-ribbon依赖,因为spring-cloud-starter-alibaba-nacos-discovery和spring-cloud-starter-openfegign默认都引入了它。
1 | |
自定义配置
例如说,自定义 Feign 的日志配置,将 Feign 的请求信息打印出来,方便排查问题。
在自定义 Feign 配置的时候,会有全局和客户端两种级别。
- 相比来说,客户端级别是更细粒度的配置。针对每个服务,Spring Cloud OpenFeign 会创建一个 Feign 客户端,并且使用服务名作为 Feign 客户端的名字。
实现 Feign 自定义配置,可以通过配置文件和 Spring JavaConfig 两种方式。
yml 配置
- Feign中的Ribbon配置:在Feign中配置Ribbon可以直接使用Ribbon的配置,具体可以参考Spring Cloud Ribbon:负载均衡的服务调用。
- Feign中的Hystrix配置:在Feign中配置Hystrix可以直接使用Hystrix的配置,具体可以参考Spring Cloud Hystrix:服务容错保护。
总结来说,这里配置名字为 demo-provider 的 Feign 客户端的日志级别为 FULL,全局级别的 Feign 客户端的日志级别为 BASIC。
1 | |
更多配置项
通过 FeignClientConfiguration 配置属性类,可以看到配置文件所支持的 FeignClient 的所有配置项。
1 | |
配置类
Feign提供了日志打印功能,可以通过配置来调整日志级别,开启更为详细的日志,从而了解Feign中Http请求的细节。
通过java配置来使Feign打印最详细的Http请求日志信息。
日志级别:
NONE:默认的,不显示任何日志;BASIC:仅记录请求方法、URL、响应状态码及执行时间;HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
1 | |
创建 DefaultFeignClientConfiguration、DemoProviderFeignClientConfiguration 配置类,FeignClient 配置类。
这两个配置类,并没有添加 @Configuration 注解。
- 因为,Spring Boot 项目默认扫描 DemoConsumerApplication 所在包以及子包下的所有 Bean 们。而
@Configuration注解也是一种 Bean,也会被扫描到。 - 如果添加了,导致整个项目的 Feign 客户端都使用相同的 Feign 配置,就无法到达 Feign 客户端级别的自定义配置的目的。
- 因此,没有添加 注解。
为了避免多个 Feign 客户端级别的配置类创建的 Bean 之间互相冲突,Spring Cloud OpenFeign 通过 FeignContext 类,为每一个 Feign 客户端创建一个 Spring 子上下文。
- 全局级别的 FeignClient 配置类是在 Spring 父上下文生效
- 客户端级别的 FeignClient 配置类在 Spring 子上下文生效。
1 | |
客户端级别
通过 @FeignClient 注解的 configuration 属性,可以设置指定 FeignClient 使用的配置类,即 Feign 客户端级别的自定义配置。因此修改DemoProviderFeignClient 代码如下图所示:

全局级别
通过 @EnableFeignClients 注解的 defaultConfiguration 属性,可以设置默认 FeignClient 使用的配置类,即 Feign 全局级别的自定义配置。因此修改 DemoConsumerApplication 代码如下图所示:

启动类 @EnableFeignClients
在启动类上添加@EnableFeignClients注解来声明开启 Feign 客户端的功能。
1 | |
使用 Ribbon 进行负载均衡
自动配置了?
Feign Ribbon 为 Feign Client重写 URL 解析,同时提供了智能路由和弹性能力。
集成 Ribbon 时,需要将 url 改为 Ribbon 的客户端名称,例如说 "myAppProd"
1 | |
UserService 接口
真正的直接消费者。
添加UserService接口完成对user-service服务的接口绑定。
DemoProviderFeignClient 接口
创建 DemoProviderFeignClient 接口,实现对服务 demo-provider 声明式调用。
-
@FeignClient注解,声明 Feign 客户端。其中name属性,为 Feign 客户端的名字,也为 Ribbon 客户端的名字,也为注册中心的服务的名字。 -
在
#echo(name)方法上,添加 SpringMVC 注解,实现对GET /demo接口的声明式调用。 -
方式一
@SpringQueryMap注解:作用相当于 Feign 的@QueryMap注解,将 POJO 对象转换成 QueryString。- 默认情况下,Feign 针对 POJO 类型的参数,即使我们声明为
GET,也会自动转换成POST类型的请求。如果去掉@SpringQueryMap注解,就会报如下异常: - Feign 自动转换成了
POST /get_demo请求,而服务提供者提供的/get_demo只支持GET类型,因此返回响应状态码为 405 的错误。
1
feign.FeignException$MethodNotAllowed: status 405 reading DemoProviderFeignClient#getDemo(DemoDTO) - 默认情况下,Feign 针对 POJO 类型的参数,即使我们声明为
-
:添加在方法上,设置请求方法和请求地址,按照@RequestLine注解请求方法 请求地址格式,例如说GET /user/get。同时,可以通过{param}表达式声明占位参数,搭配@Param注解一起使用。 -
:添加在方法上,设置请求头。@Headers注解- 这里,使用在
#add(UserAddRequest request)方法上,因为 HTTP 接口/user/add使用@RequestBody+ JSON 格式接收参数。
- 这里,使用在
1 | |
添加 @FeignClient
- 通过@FeignClient注解实现一个Feign客户端,其中的 value 表示这是对user-service服务的接口调用客户端。
可以回想下user-service中的UserController,只需将其改为接口,保留原来的SpringMvc注释即可。
1 | |
UserFallbackService
服务降级使用起来非常方便,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可。
需要注意的是它实现了UserService接口,并且对接口中的每个实现方法进行了服务降级逻辑的实现。
下面为UserService接口、添加一个服务降级实现类 UserFallbackService。
1 | |
UserFeignController
创建 ConsumerController 类,提供一个通过 Feign 调用服务提供者的 HTTP 接口。
添加UserFeignController调用UserService实现服务调用。
直接访问服务消费者的 http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口,返回结果为
consumer:16445-provider:yudaoyuanma。能够调通 HTTP 接口,说明使用继承特性成功。
1 | |
Feign 单独使用
在使用 Spring Cloud 的项目中,大多数是通过 Feign 调用从 Ribbon 负载均衡选择的服务实例,而 Ribbon 是通过注册中心获取到的服务实例列表。但是有些场景下,可能想要单独使用 Feign 调用,例如说:
- 调用第三方服务,例如说短信云服务、推送云服务。
- 调用的虽然是内部服务,但是并没有注册到注册中心,而是使用 Nginx 代理并负载均衡实现高可用。
DemoProviderFeignClient
将 @FeignClient 注解的 url 属性设置要调用的服务的地址。
- 不过要注意,保持
name属性和url属性的 host 是一致的,不然还是会使用 Ribbon 进行负载均衡。
比如,修改 DemoProviderFeignClient 接口,改成调用 http://www.iocoder.cn。
1 | |
Hystrix
Feign Hystrix 使用 Hystrix 提供了断路器的支持。
若要使用 Hystrix 时,需要将 Hystrix 模块加入 classpath 中。然后,使用 HystrixFeign.builder。
1 | |
功能演示
负载均衡功能
- 启动eureka-service,两个user-service,feign-service服务,启动后注册中心显示如下:

- 多次调用http://localhost:8701/user/1进行测试,可以发现运行在8201和8202的user-service服务交替打印如下信息:
1 | |
服务降级功能
-
关闭两个user-service服务,重新启动feign-service;
-
调用http://localhost:8701/user/1进行测试,可以发现返回了服务降级信息。

日志打印功能
调用http://localhost:8701/user/1进行测试,可以看到以下日志。
- 从日志中也可以看出 Feign 是调用日志组件的
DEBUG级别打印日志。
1 | |
HTTP 客户端
默认情况下,Feign 通过 JDK 自带的 HttpURLConnection 封装了 Client.Default,实现 HTTP 调用的客户端。因为 HttpURLConnection 缺少对 HTTP 连接池的支持,所以性能较低,在并发到达一定量级后基本会出现。
因此 Feign 提供了另外两个 HTTP 客户端:
- ApacheHttpClient,基于 Apache HttpClient 封装
- OkHttpClient,基于 OkHttp 封装
请求重试
Feign 和 Ribbon 都有请求重试的功能,两者都启用该功能的话,会产生冲突的问题。因此,有且只能启动一个的重试。
-
目前比较推荐的是使用 Ribbon 来提供重试。
-
在 Spring Cloud OpenFeign 中,默认创建的是 NEVER_RETRY 不进行重试。如此,只需要配置 Ribbon 的重试功能即可。
例如,搭建下 Feign + Ribbon 请求重试的使用示例。