Appearance
OpenFeign 极简教程
一、基础理论
1. 定位
- 声明式 HTTP 客户端:接口 + 注解发起远程调用,无需手写 HTTP 请求。
- 核心角色:承担微服务间的同步通信与负载均衡(需配合 LB)。
2. 核心原理
- 动态代理:启动时为
@FeignClient接口生成 JDK 动态代理 Bean。 - 方法调用 → HTTP 请求:调用接口方法时,由
InvocationHandler拦截,通过 Contract 解析注解构造请求模板(RequestTemplate)。 - 编码/解码:请求体经 Encoder(默认 Jackson)序列化;响应经 Decoder 反序列化。
- 拦截器:可通过
RequestInterceptor统一加 Header(如 Token)。 - 底层 HTTP 客户端:默认
HttpURLConnection,生产中替换为 OkHttp 或 Apache HttpClient。
3. 完整调用链路
- Consumer 注入
@FeignClient代理对象。 - 发起方法调用 →
ReflectiveFeign.FeignInvocationHandler拦截。 - 解析注解(
@GetMapping、@PathVariable等)构建RequestTemplate。 RequestInterceptor拦截补充 Header。- 经 LoadBalancer(
ReactorLoadBalancerExchangeFilterFunction或旧版Ribbon)从注册中心选择一个目标实例。 - Encoder 序列化请求体,驱动底层 HTTP 客户端发送请求。
- Decoder 反序列化响应,返回给业务代码。
4. 负载均衡演进
- Spring Cloud 2020 之前:默认集成 Ribbon(
IRule负载均衡)。 - Spring Cloud 2020+:Ribbon 进入维护模式,官方迁移至 Spring Cloud LoadBalancer(Reactor-based,配合
BlockingLoadBalancerClient)。
5. 降级与熔断
- Fallback:服务异常时返回兜底结果,需实现 Feign 接口。
- FallbackFactory:功能同上,且能捕获异常堆栈,推荐生产使用。
- 推荐使用 Sentinel开启熔断。
6. 经典对比
| 维度 | RestTemplate | OpenFeign | WebClient |
|---|---|---|---|
| 编码风格 | 模板代码多 | 声明式,接口即客户端 | 函数式响应式 |
| 可读性 | 一般 | 高 | 较高 |
| 同步/异步 | 同步 | 同步 | 异步非阻塞 |
| 负载均衡 | 需手动配合 LB | 内置集成 | 需手动配合 |
| 适用场景 | 简单调用 | 微服务间同步调用 | 网关/高并发流式场景 |
选型建议:内部服务同步调用用 OpenFeign;网关层或超高并发场景用 WebClient。
二、工作实践(复制即用)
1. 基础依赖
xml
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 负载均衡器(Spring Cloud 2020+ 必须显式引入) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>2. 声明式接口(最简版)
java
@FeignClient(
name = "order-service", // 目标服务名(注册中心)
path = "/api/v1", // 统一前缀(可选)
configuration = FeignConfig.class // 局部配置(可选)
)
public interface OrderClient {
@GetMapping("/order/{id}")
OrderDTO getOrder(@PathVariable("id") Long id);
@PostMapping("/order")
OrderDTO createOrder(@RequestBody OrderCreateReq req);
}3. 消费者调用
启动类加注解
java
@SpringBootApplication
@EnableFeignClients(basePackages = "com.xxx.client")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}Controller 直接注入使用
java
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private OrderClient orderClient;
@GetMapping("/order/{id}")
public OrderDTO getOrder(@PathVariable Long id) {
return orderClient.getOrder(id);
}
}4. 超时与日志配置(推荐 YAML)
yaml
spring:
cloud:
openfeign:
client:
config:
default: # default 表示全局;写 service-id 可针对单个服务
connectTimeout: 2000 # 连接超时(毫秒)
readTimeout: 5000 # 读取超时(毫秒)
loggerLevel: full # none/basic/headers/full
# 配合日志级别打印 Feign 请求明细
logging:
level:
com.xxx.client.OrderClient: debug5. 降级容错(FallbackFactory)
实现 FallbackFactory
java
@Slf4j
@Component
public class OrderClientFallbackFactory implements FallbackFactory<OrderClient> {
@Override
public OrderClient create(Throwable cause) {
log.error("调用 order-service 失败", cause);
return new OrderClient() {
@Override
public OrderDTO getOrder(Long id) {
// 返回兜底数据或抛业务异常
OrderDTO dto = new OrderDTO();
dto.setId(id);
dto.setName("降级订单");
return dto;
}
@Override
public OrderDTO createOrder(OrderCreateReq req) {
throw new RuntimeException("创建订单服务暂不可用");
}
};
}
}接口注解指定
java
@FeignClient(
name = "order-service",
fallbackFactory = OrderClientFallbackFactory.class
)
public interface OrderClient { ... }6. Header 透传示例(如传递 Token)
全局拦截器
java
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
// 从当前线程上下文(如 Session/TransmittableThreadLocal)取 Token
String token = RequestContext.getToken();
if (StrUtil.isNotBlank(token)) {
template.header("Authorization", "Bearer " + token);
}
// 透传 traceId
String traceId = MDC.get("traceId");
if (StrUtil.isNotBlank(traceId)) {
template.header("X-Trace-Id", traceId);
}
};
}
}三、核心速查表
| 注解/配置 | 说明 |
|---|---|
@EnableFeignClients | 启动类开启 Feign 扫描 |
@FeignClient | 声明接口为 Feign 客户端;name=服务名,fallbackFactory=降级工厂 |
@PathVariable | URL 路径参数(必须显式指定 value) |
@RequestParam | URL 查询参数 |
@RequestBody | POST JSON 请求体 |
@RequestHeader | 单方法级别传 Header |
RequestInterceptor | 全局拦截器,用于统一加 Header/签名 |
connectTimeout | 建立 TCP 连接的超时时间 |
readTimeout | 等待响应的超时时间 |
loggerLevel | none/basic/headers/full(调试时用 full) |
四、排错速查
| 现象 | 解决 |
|---|---|
No qualifying bean of type 'OrderClient' | 启动类没加 @EnableFeignClients;或接口不在扫描路径下 |
LoadBalancer does not have available server | 1. 目标服务未注册到注册中心 2. 未引入 spring-cloud-starter-loadbalancer3. 服务名拼写错误 |
| 请求返回 404 | 检查 @FeignClient(path="xxx") 与方法的 @GetMapping 拼接后的完整路径 |
| 中文乱码 / 提交失败 | POST 请求对象忘记加 @RequestBody |
| 超时不起作用 | 确认配置在 spring.cloud.openfeign.client.config.xxx 下,而非 feign.client(旧版) |
| Fallback 不触发 | 需引入熔断组件(如 Sentinel)并开启 feign.sentinel.enabled=true |