外观
一句话答案
Feign 是声明式 HTTP 客户端,@FeignClient 接口通过 JDK 动态代理生成 HTTP 调用,整合负载均衡和熔断。
核心要点
OpenFeign 是什么?
OpenFeign 是一个声明式 HTTP 客户端,用接口 + 注解的方式定义 HTTP 调用,无需手写 RestTemplate 代码:
java
// 定义 Feign 客户端接口(无需实现类)
@FeignClient(name = "order-service", fallbackFactory = OrderClientFallback.class)
public interface OrderClient {
@GetMapping("/api/orders/{id}")
OrderDTO getOrder(@PathVariable("id") Long id);
@PostMapping("/api/orders")
OrderDTO createOrder(@RequestBody CreateOrderRequest request);
}
// 在 Service 中直接注入使用
@Service
public class PaymentService {
@Autowired
private OrderClient orderClient; // 注入接口,底层是动态代理
public void pay(Long orderId) {
OrderDTO order = orderClient.getOrder(orderId); // 像调本地方法一样
}
}OpenFeign 底层调用流程:
① @EnableFeignClients 启动扫描
→ 扫描所有 @FeignClient 注解的接口
→ 为每个接口注册一个 FeignClientFactoryBean 到 Spring 容器
② FeignClientFactoryBean.getObject()
→ 使用 Feign.Builder 构建代理对象
→ JDK 动态代理 / CGLIB 生成接口的代理实现类
③ 方法调用触发 InvocationHandler
→ 解析方法上的 @GetMapping / @PostMapping 注解
→ 构建 RequestTemplate(URL、HTTP Method、Header、Body)
→ 将 @PathVariable / @RequestParam / @RequestBody 参数填入模板
④ 请求编码(Encoder)
→ 将请求体序列化为 JSON(默认 Jackson / 可配 Gson)
⑤ 负载均衡(LoadBalancer 拦截)
→ 将 "order-service" 解析为实际 IP:Port
→ 从 Nacos 本地缓存获取实例列表
→ 通过 Spring Cloud LoadBalancer 选择一个实例
→ 替换 URL:http://order-service/api/orders/1 → http://192.168.1.2:8080/api/orders/1
⑥ 发送 HTTP 请求
→ 默认使用 JDK HttpURLConnection(同步阻塞)
→ 可替换为 Apache HttpClient / OkHttp(支持连接池,推荐生产使用)
⑦ 响应解码(Decoder)
→ 将 HTTP 响应体反序列化为 Java 对象(OrderDTO)
⑧ 异常处理
→ 如果 HTTP 状态码非 2xx → 触发 ErrorDecoder
→ 如果调用超时/失败 → 触发重试机制 / 降级 fallback生产级优化配置:
yaml
feign:
client:
config:
default:
connectTimeout: 3000 # 连接超时 3s
readTimeout: 5000 # 读取超时 5s
loggerLevel: BASIC # 日志级别
compression:
request:
enabled: true # 请求压缩
response:
enabled: true # 响应压缩
okhttp:
enabled: true # 使用 OkHttp 替换默认 HttpURLConnection四、配置中心
追问与易错
追问方向:
- Feign 超时怎么配?
- 日志级别有哪些?
- 和 RestTemplate 比优势?
易错点:
- ❌ Feign 性能很差——底层可换 OkHttp
- ❌ Feign 只能调 SC 服务——可以调任何 HTTP
💡 记忆锚点
Feign让远程调用看起来像调本地方法:写个接口加@FeignClient注解,底层JDK动态代理拦截调用 -> 解析注解构建HTTP请求 -> LoadBalancer选实例替换URL -> 发送HTTP -> 反序列化返回。生产记得换OkHttp(连接池),配好超时和降级fallback。