FeignRPC源码分析
1. 简介
Feign 是一个声明式的 HTTP 客户端库,由 Netflix 开发,现属于 OpenFeign 项目。它简化了 HTTP API 客户端的编写,让你可以用简单的接口和注解来定义 REST 服务调用。
1. 核心特点
- 声明式风格 :通过 Java 接口和注解定义 HTTP 请求
- 简化开发 :自动处理 HTTP 请求/响应序列化
- 集成友好 :与 Ribbon、Hystrix、Eureka 等 Netflix 组件无缝集成
- 可扩展 :支持自定义编码器、解码器、拦截器等
2. 声明式风格
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
// 使用HttpClient等工具手动构建请求
public List<User> getUsers() {
try {
// 1. 创建HTTP客户端
CloseableHttpClient client = HttpClients.createDefault();
// 2. 构建请求
HttpGet request = new HttpGet("http://api.example.com/users");
request.addHeader("Accept", "application/json");
// 3. 执行请求
HttpResponse response = client.execute(request);
// 4. 处理响应
String json = EntityUtils.toString(response.getEntity());
// 5. 手动反序列化
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, new TypeReference<List<User>>(){});
} catch (Exception e) {
throw new RuntimeException("API调用失败", e);
}
}
// 只需定义接口,Feign自动实现
public interface UserApi {
@RequestLine("GET /users")
@Headers("Accept: application/json")
List<User> getUsers();
}
// 使用方式
public class Client {
public static void main(String[] args) {
UserApi api = Feign.builder()
.decoder(new JacksonDecoder())
.target(UserApi.class, "http://api.example.com");
List<User> users = api.getUsers(); // 一行调用
}
}
基于 interface + annotation 实现了 DSL,只需要描述接口信息,其他的样板代码如创建httpclient、payload编解码、
只需要描述 调用的接口,具体的调用细节全部屏蔽。
2. 核心API
1. Feign
1,【构造Feign】feign.Feign.Builder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建完整配置的Feign实例
public static Feign createFeignInstance() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return Feign.builder()
.client(new ApacheHttpClient())
.encoder(new JacksonEncoder(objectMapper))
.decoder(new JacksonDecoder(objectMapper))
.errorDecoder(new CustomErrorDecoder())
.options(new Request.Options(1000, 5000))
.retryer(new Retryer.Default())
.logger(new Slf4jLogger())
.logLevel(Logger.Level.BASIC)
.requestInterceptor(new AuthInterceptor())
.queryMapEncoder(new BeanQueryMapEncoder())
.contract(new SpringMvcContract())
.build();
}
feign.Feign.Builder是可复用的工厂,收集了大量定制化配置 和 自定义拓展,用于后续指定Interface动态代理生成目标对象。
.client(new ApacheHttpClient())- 作用:指定底层HTTP客户端实现
- 说明:使用Apache HttpClient作为HTTP传输层,替代默认的JDK HttpURLConnection
.encoder(new JacksonEncoder(objectMapper))- 作用:请求体编码器
- 说明:使用Jackson将Java对象序列化为JSON格式的请求体
- 定制:传入自定义配置的ObjectMapper(支持Java8时间类型,忽略未知属性)
.decoder(new JacksonDecoder(objectMapper))- 作用:响应体解码器
- 说明:使用Jackson将JSON响应体反序列化为Java对象
- 定制:使用与编码器相同的ObjectMapper配置保证一致性
.errorDecoder(new CustomErrorDecoder())- 作用:错误响应处理器
- 说明:自定义非2xx响应的处理逻辑,可转换为特定异常类型
.options(new Request.Options(1000, 5000))- 作用:请求超时配置
- 参数:连接超时1秒,读取超时5秒
.retryer(new Retryer.Default())- 作用:重试策略
- 说明:使用默认的重试机制(间隔时间指数增长,最多重试5次)
.logger(new Slf4jLogger())- 作用:日志记录器
- 说明:使用SLF4J记录请求日志
.logLevel(Logger.Level.BASIC)- 作用:日志级别
- 说明:记录请求方法、URL和响应状态等基本信息
.requestInterceptor(new AuthInterceptor())- 作用:请求拦截器
- 说明:可添加认证头等全局请求参数(如JWT token)
.queryMapEncoder(new BeanQueryMapEncoder())- 作用:查询参数编码器
- 说明:将Java对象转换为URL查询参数
.contract(new SpringMvcContract())- 作用:接口方法解析契约
- 说明:支持Spring MVC注解(如@RequestMapping),使Feign接口与Controller定义方式一致
2.【构造Stub】feign.Feign
1
2
3
4
5
6
// 创建目标客户端
public static <T> T createClient(Class<T> apiType, String baseUrl) {
return createFeignInstance().newInstance(
new Target.HardCodedTarget<>(apiType, baseUrl)
);
}
3. 应用层使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface MyApi {
// 1. 基本GET请求
@RequestLine("GET /users/{id}")
User getUserById(@Param("id") Long userId);
}
// 用户实体类
class User {
private Long id;
private String name;
private String email;
// getters/setters 省略
}
// 使用示例
public class Main {
public static void main(String[] args) {
MyApi api = FeignClientFactory.createClient(
MyApi.class,
"http://api.example.com"
);
api.getData(); // 调用接口方法
}
}
2. Target
Feign动态代理时,需要传入一个target。
是动态代理构造Stub的核心上下文。
apply方法是一个hook点,Stub发起调用时,流程会经过该hook点,允许拓展。
3. Client
Client是负责 发出请求、接受响应的 可拔插 拓展点。具体是远程调用还是本地调用、具体是HTTP协议还是其他协议对于上层透明。
只要能够处理Request,返回Response即可。
常见实现类有:
4. RequestTemplate
feign.RequestTemplate是 Request的工厂,基于【初始化时静态的语法元素】+【调用时动态的方法参数】来动态构造最终的Request。
3. 动态代理
1. newInstance(动态代理)
2. targetToHandlersByName.apply(target)
3. parseAndValidatateMetadata
DSL契约拓展,识别各个预定义的注解。
输出是各个方法的Metadata:
基于 方法、注解等静态的元信息,构造出来一个静态的 ResquestTemplate。
这个ResquestTemplate 只是一个模板,还有很多占位符需要再 方法调用时 填充。
默认的契约解析只识别Feign定义的HTTP要素注解:
可自行拓展适配其他注解,比如SpringMVC的一整套HTTP要素注解(RequestMapping等)。
4. 调用流程
1. InvocationHandler
2. MethodHandler
1. 调用入口
2. 克隆、填充请求模板
3. 调用流程
5. Spring Cloud封装
1. FeignClientFactoryBean
运行时扫描所有携带FeignClient的接口,构造FeignClientFactoryBean。
2. 独立的、隔离的IOC容器
3. 组件关系
- 每个
@FeignClient注解的接口都会获得一个独立的子上下文 - 子上下文默认继承父上下文的所有bean定义
- 子上下文可以通过
@Configuration类覆盖父上下文的bean定义 - 这种设计实现了配置隔离,允许不同的Feign客户端有不同的行为
- 子上下文的生命周期由
FeignContext管理,随应用启动而创建,随应用关闭而销毁
这种设计模式使得Spring Cloud OpenFeign能够灵活地支持微服务架构中不同服务客户端的不同配置需求。
6. 总结
Feign 通过声明式接口 + 注解 将 HTTP 客户端调用简化为本地方法调用,屏蔽了底层 HTTP 请求的复杂性(如序列化、连接管理、重试等),显著提升开发效率。
1. 设计思想
- 动态代理 :运行时生成接口的实现类,将方法调用转换为 HTTP 请求。
- 契约模式 :通过注解(如
@RequestLine)定义 HTTP 语义,支持扩展(如适配 Spring MVC 注解)。 - 组件化 :所有关键步骤(编码、解码、HTTP 客户端等)均可插拔替换,高度可定制。
2. 最佳实践
- 统一配置 :通过
Feign.Builder集中管理超时、编解码等。 - 错误处理 :自定义
ErrorDecoder将 HTTP 错误转换为业务异常。 - 性能优化 :选择高性能 HTTP 客户端(如 OkHttp),合理设置连接池。





























