Skip to content

OpenFeign 极简教程

一、基础理论

1. 定位

  • 声明式 HTTP 客户端:接口 + 注解发起远程调用,无需手写 HTTP 请求。
  • 核心角色:承担微服务间的同步通信负载均衡(需配合 LB)。

2. 核心原理

  • 动态代理:启动时为 @FeignClient 接口生成 JDK 动态代理 Bean。
  • 方法调用 → HTTP 请求:调用接口方法时,由 InvocationHandler 拦截,通过 Contract 解析注解构造请求模板(RequestTemplate)。
  • 编码/解码:请求体经 Encoder(默认 Jackson)序列化;响应经 Decoder 反序列化。
  • 拦截器:可通过 RequestInterceptor 统一加 Header(如 Token)。
  • 底层 HTTP 客户端:默认 HttpURLConnection,生产中替换为 OkHttpApache HttpClient

3. 完整调用链路

  1. Consumer 注入 @FeignClient 代理对象。
  2. 发起方法调用 → ReflectiveFeign.FeignInvocationHandler 拦截。
  3. 解析注解(@GetMapping@PathVariable 等)构建 RequestTemplate
  4. RequestInterceptor 拦截补充 Header。
  5. LoadBalancerReactorLoadBalancerExchangeFilterFunction 或旧版 Ribbon)从注册中心选择一个目标实例。
  6. Encoder 序列化请求体,驱动底层 HTTP 客户端发送请求。
  7. Decoder 反序列化响应,返回给业务代码。

4. 负载均衡演进

  • Spring Cloud 2020 之前:默认集成 RibbonIRule 负载均衡)。
  • Spring Cloud 2020+:Ribbon 进入维护模式,官方迁移至 Spring Cloud LoadBalancer(Reactor-based,配合 BlockingLoadBalancerClient)。

5. 降级与熔断

  • Fallback:服务异常时返回兜底结果,需实现 Feign 接口。
  • FallbackFactory:功能同上,且能捕获异常堆栈,推荐生产使用。
  • 推荐使用 Sentinel开启熔断。

6. 经典对比

维度RestTemplateOpenFeignWebClient
编码风格模板代码多声明式,接口即客户端函数式响应式
可读性一般较高
同步/异步同步同步异步非阻塞
负载均衡需手动配合 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: debug

5. 降级容错(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=降级工厂
@PathVariableURL 路径参数(必须显式指定 value)
@RequestParamURL 查询参数
@RequestBodyPOST JSON 请求体
@RequestHeader单方法级别传 Header
RequestInterceptor全局拦截器,用于统一加 Header/签名
connectTimeout建立 TCP 连接的超时时间
readTimeout等待响应的超时时间
loggerLevelnone/basic/headers/full(调试时用 full)

四、排错速查

现象解决
No qualifying bean of type 'OrderClient'启动类没加 @EnableFeignClients;或接口不在扫描路径下
LoadBalancer does not have available server1. 目标服务未注册到注册中心
2. 未引入 spring-cloud-starter-loadbalancer
3. 服务名拼写错误
请求返回 404检查 @FeignClient(path="xxx") 与方法的 @GetMapping 拼接后的完整路径
中文乱码 / 提交失败POST 请求对象忘记加 @RequestBody
超时不起作用确认配置在 spring.cloud.openfeign.client.config.xxx 下,而非 feign.client(旧版)
Fallback 不触发需引入熔断组件(如 Sentinel)并开启 feign.sentinel.enabled=true