type
status
date
slug
summary
tags
category
password
1、Feign核心架构
Feign 是一个声明式的 HTTP 客户端框架,主要用于简化微服务之间的远程调用,使得开发者可以通过简单的接口定义实现复杂的 HTTP 远程调用。
1.1 Feign的原理
Feign 调用的原理:通过一系列的封装和注解,讲
@FeignClient
注解修饰的接口方法,转换成调用远程服务的 HTTP 请求,经过均衡负载器的处理将请求发送给远程服务,并解析请求结果返回给调用者,调用者在调用远程服务的方法的时候感觉就跟调用本地方法一样透明。Feign 的原理是基于 JDK 动态代理机制,由以下几个关键组件构成:
- 接口定义层:开发者通过 Java 接口定义服务契约
- 动态代理层:在运行时生成接口的实现类
- 编码/解码层:负责请求/响应的序列化和反序列化
- HTTP调用层:实际执行 HTTP 请求
- 配置和扩展层:提供各种配置选项和扩展点
Feign 通过这种高度模块化的设计,使得各个组件都可以灵活替换和扩展,能够适应各种复杂的业务场景。
1.2 Feign的核心组件
- Contract:负责解析接口方法上的注解,定义了如何将注解转换为 MethodMetadata,即将注解转换为 Feign 内部表示
- 默认实现是
SpringMvcContract
(Spring Cloud环境下)
- Encoder/Decoder:编解码器
- Encoder:负责将 Java 对象编码为 HTTP 请求体,默认实现:
SpringEncoder
(Spring环境下) - Decoder:负责将 HTTP 响应解码为 Java 对象,默认实现:
SpringDecoder
(Spring环境下)
- Logger:提供日志记录功能,可以记录请求和响应的详细信息。
- Client:实际执行 HTTP 请求的底层客户端:
- 默认实现:
Client.Default
(基于 HttpURLConnection) - 其他常见实现:
ApacheHttpClient
、OkHttpClient
、RibbonClient
等
- Retryer:提供重试机制,可以自定义重试策略
- 默认实现:
Retryer.Default
- RequestInterceptor:请求拦截器,可以在请求发送前对请求进行修改,常用于添加统一的请求头,支持多个拦截器链式调用
2、Feign调用的流程
Feign 的调用流程包括以下阶段:
- 接口定义:使用
@FeignClient
注解声明远程调用的接口,并指定服务名、URL 或降级逻辑。
- 动态代理生成:应用启动时扫描
@FeignClient
接口,生成 JDK 动态代理。代理对象的核心是FeignInvocationHandler
(基于InvocationHandler
接口),拦截所有方法调用。
- 请求构造:方法调用触发代理逻辑,构造
RequestTemplate
和完整的 HTTP 请求模板(包括 URL、Headers、Body)。
- 负载均衡:从注册中心(Eureka/Nacos)获取服务列表,根据均衡负载器(Ribbon/LoadBalancer)的策略(轮询、随机等)选择一个实例,然后替换 URL 中的服务名为实例的 IP:Port。
- HTTP 请求执行:使用 HTTP 客户端(HttpURLConnection/OkHttp/Apache HttpClient)执行 HTTP 请求。
- 响应解析:解析响应体,如果请求成功,将 JSON 响应体序列化为 Java 对象(默认使用 Jackson),将 Java 对象返回给调用方;如果请求失败,会抛出
FeignException
异常或触发熔断降级逻辑。

2.1 Feign调用的初始化过程
2.1.1 注解驱动机制
Feign的启动始于
@EnableFeignClients
注解,该注解一般放在启动类上,通过FeignClientsRegistrar
类实现接口扫描和动态代理创建:FeignClientsRegistrar
实现了ImportBeanDefinitionRegistrar
接口,在registerBeanDefinitions
方法中完成:- 扫描所有被
@FeignClient
标记的接口
- 为每个接口创建
FeignClientFactoryBean
工厂Bean
- 注册 BeanDefinition 到 Spring 容器
2.1.2 代理对象创建流程
当 Spring 容器初始化 Feign客户端时,会调用
FeignClientFactoryBean.getObject()
方法,核心代码如下:最终会调用
ReflectiveFeign.newInstance()
方法创建 JDK 动态代理对象2.2 动态代理与调用拦截机制
2.2.1 代理类生成
ReflectiveFeign.newInstance()
方法核心逻辑:这里创建的
InvocationHandler
实例,是 Feign 自己基于 java.lang.reflect
包中的 InvocationHandler
接口实现的代理类 FeignInvocationHandler
,这个代理类是 ReflectiveFeign
的内部类。这个内部类的两个核心关注点:
dispatch
属性:是一个Map<Method, MethodHandler>
,存储了每个方法对应的处理器。
invoke
方法:选择方法对应的处理器执行方法。
2.2.2 方法调用拦截
当调用 Feign 接口方法时,会进入
FeignInvocationHandler.invoke()
方法,这里会选择方法对应的 MethodHandler
的 invoke
方法,完成实际的 HTTP 请求和结果的处理。MethodHandler
是 Feign 定义的独立的接口,定义在 InvocationHandlerFactory
接口中,仅仅拥有一个 invoke
方法。MethodHandler
的invoke 方法,主要职责是完成实际远程URL请求,然后返回解码后的远程 URL 的响应结果。Feign 提供了默认的 SynchronousMethodHandler
实现类,提供了基本的远程 URL 的同步请求处理。
2.3 请求构建与执行流程
2.3.1 请求模板构建
SynchronousMethodHandler.invoke()
是实际处理HTTP请求的核心:SynchronousMethodHandler
的 invoke
方法,调用了自己的 executeAndDecode
请求执行和结果解码方法。该方法的工作步骤:- 首先通 RequestTemplate 请求模板实例,生成远程 URL 请求实例 request;
- 然后用自己的 feign 客户端 client 属性,
excecute
执行请求,并且获取 response 响应;
- 对 response 响应进行结果解码。
这里的 client 属性,基于 Feign 自定义的
Client
接口,这是 Feign 中一个非常重要的组件,负责端到端的执行 URL 请求。2.3.2 HTTP客户端执行
Client
接口是客户端的顶层接口支持多种 HTTP 客户端实现:
Client.Default
:默认的feign.Client
客户端实现类,内部使用HttpURLConnnection
完成 URL 请求处理。
ApacheHttpClient
:基于 Apache HttpClient 开源组件的的 feign.Client 客户端实现类,需要另外引入feign-httpclient
依赖。
OkHttpClient
:基于 OkHttp3 开源组件的 feign.Client 客户端实现类,需要另外引入feign-okhttp
依赖。
FeignBlockingLoadBalancerClient
:基于 LoadBalancer 负载均衡的 feign.Client 客户端实现类,通过LoadBalancerClient
选择服务实例,重构请求 URL 后委托给底层 HTTP 客户端(如 Apache HttpClient 或 OkHttp)执行请求。
在 Spring Cloud 环境中,默认使用
FeignBlockingLoadBalancerClient
实现负载均衡。2.3.2 请求拦截器处理
请求会经过一系列拦截器处理,拦截器的顶层接口是
RequestInterceptor
。内置拦截器包括:
BasicAuthRequestInterceptor
:处理基础认证
FeignAcceptGzipEncodingInterceptor
:处理编码类型
FeignContextGzipEncodingInterceptor
:处理内容压缩
2.4 均衡负载处理
均衡负载器的顶级接口是
LoadBalancerClient
,这里面最重要的是 choose
方法和 execute
方法。LoadBalancerClient
接口的默认实现类是 BlockingLoadBalancerClient
,这一个同步/阻塞式的客户端均衡负载实现。他的作用有:- 服务实例选择:根据服务ID从注册中心获取可用实例列表
- 负载均衡策略:应用配置的负载均衡算法(如轮询、随机等),默认为轮询(实现类为
RoundRobinLoadBalancer
)
- 服务调用:自动将
@FeignClient
的服务名解析为实际地址。
2.5 调用流程时序图
写一个最简单的远程调用例子,消费者控制器
ConsumerController
@FeignClient
注解修饰的接口生产者控制器
ProviderController
当调用
ConsumerController.helloFeign()
方法时,整个流程时序图如下所示:
3、性能优化建议
1、使用连接池
使用 Apache HttpClient 或 OkHttp 替换默认的 HttpURLConnection。以 Apache HttpClient 为例:
第一步,引入依赖
第二步,启用配置,在 application.yml 中配置:
第三步,自定义配置
2、合理配置超时
3、启用压缩:
4、缓存设计:对不变的结果进行适当缓存
5、日志优化:合理设置日志级别避免性能损耗
- Author:mcbilla
- URL:http://mcbilla.com/article/1dc85c7d-7c1d-808f-aed9-dc5c58b2942f
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!