编辑
2026-09-05
后端
00
请注意,本文编写于 40 天前,最后修改于 5 天前,其中某些信息可能已经过时。

目录

集群架构
分布式
nacos
1. OpenFeign 是什么
2. 常见使用场景
3. 引入依赖
4. 开启 OpenFeign
5. 创建 FeignClient
6. @FeignClient 常用参数
name
url
path
configuration
7. GET 请求
Path 参数
Query 参数
8. POST 请求
JSON 请求体
9. PUT 请求
10. DELETE 请求
11. 请求头 Header
12. Feign 请求拦截器
13. 传递当前请求 Token
14. 配置超时时间
15. 单独配置某个服务
16. Feign 日志
17. 使用 DTO
18. 统一返回结构
19. 服务注册中心调用
20. OpenFeign 常见项目结构
21. FeignClient 命名建议
22. 接口定义与 FeignClient
23. 常见错误
错误一:忘记开启 EnableFeignClients
错误二:PathVariable 没有写 name
错误三:RequestParam 没写名称
错误四:服务名称不一致
错误五:Feign 超时
错误六:401 / 403
24. OpenFeign 与 RestTemplate 对比
25. OpenFeign 调用流程
26. 推荐使用方式
27. 一个完整示例
FeignClient
Service
28. 一句话理解 OpenFeign
Sentinel

集群架构

image.png

分布式

image.png

image.png

nacos

image.png

1. OpenFeign 是什么

OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端。

它可以通过定义 Java 接口和注解的方式,直接调用其他服务提供的 HTTP 接口,不需要手动编写 HTTP 请求代码。

例如传统方式可能需要:

java
RestTemplate restTemplate = new RestTemplate(); User user = restTemplate.getForObject( "http://user-service/user/1", User.class );

使用 OpenFeign 后,只需要定义接口:

java
@FeignClient(name = "user-service") public interface UserClient { @GetMapping("/user/{id}") User getUser(@PathVariable("id") Long id); }

调用时:

java
User user = userClient.getUser(1L);

OpenFeign 会自动发送 HTTP 请求。


2. 常见使用场景

OpenFeign 一般用于微服务之间的接口调用。

例如:

text
订单服务 ↓ 调用用户服务 order-service ↓ OpenFeign user-service

订单服务需要获取用户信息时,可以直接通过 FeignClient 调用用户服务。

适合:

  • 微服务内部接口调用
  • Spring Cloud 项目
  • 服务注册中心环境
  • REST API 调用
  • 对第三方 HTTP API 进行统一封装

3. 引入依赖

Spring Boot / Spring Cloud 项目中引入:

xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

一般建议 Spring Boot 与 Spring Cloud 使用对应的兼容版本。


4. 开启 OpenFeign

在 Spring Boot 启动类中添加:

java
@EnableFeignClients @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

核心注解:

java
@EnableFeignClients

作用:

扫描项目中的 @FeignClient 接口,并创建对应的代理对象。


5. 创建 FeignClient

例如需要调用用户服务:

java
@FeignClient(name = "user-service") public interface UserFeignClient { @GetMapping("/user/{id}") UserDTO getUserById(@PathVariable("id") Long id); }

调用:

java
@Service public class OrderService { private final UserFeignClient userFeignClient; public OrderService(UserFeignClient userFeignClient) { this.userFeignClient = userFeignClient; } public UserDTO getUser(Long userId) { return userFeignClient.getUserById(userId); } }

6. @FeignClient 常用参数

基本写法:

java
@FeignClient(name = "user-service")

name

表示服务名称。

例如:

java
@FeignClient(name = "user-service")

如果项目接入 Nacos、Eureka 等注册中心:

text
user-service

会对应注册中心里的服务名称。

OpenFeign 会根据服务名找到对应服务实例。


url

如果不是通过注册中心调用,而是直接调用固定地址,可以使用:

java
@FeignClient( name = "userClient", url = "http://localhost:8081" )

例如:

java
@FeignClient( name = "weatherClient", url = "https://api.example.com" ) public interface WeatherClient { @GetMapping("/weather") String getWeather(); }

path

可以统一配置接口前缀:

java
@FeignClient( name = "user-service", path = "/api/user" ) public interface UserFeignClient { @GetMapping("/{id}") UserDTO getUser(@PathVariable("id") Long id); }

最终请求:

text
/api/user/1

configuration

可以为指定 FeignClient 配置单独的配置类:

java
@FeignClient( name = "user-service", configuration = FeignConfig.class )

例如配置:

  • 请求拦截器
  • 日志级别
  • Encoder
  • Decoder
  • ErrorDecoder

7. GET 请求

Path 参数

接口:

http
GET /user/1001

Feign:

java
@GetMapping("/user/{id}") UserDTO getUser( @PathVariable("id") Long id );

调用:

java
userFeignClient.getUser(1001L);

Query 参数

接口:

http
GET /user/list?page=1&size=20

Feign:

java
@GetMapping("/user/list") List<UserDTO> listUser( @RequestParam("page") Integer page, @RequestParam("size") Integer size );

调用:

java
userFeignClient.listUser(1, 20);

8. POST 请求

JSON 请求体

接口:

http
POST /user Content-Type: application/json

请求体:

json
{ "name": "张三", "age": 20 }

Feign:

java
@PostMapping("/user") UserDTO createUser( @RequestBody CreateUserDTO dto );

调用:

java
CreateUserDTO dto = new CreateUserDTO(); dto.setName("张三"); dto.setAge(20); UserDTO user = userFeignClient.createUser(dto);

9. PUT 请求

java
@PutMapping("/user/{id}") UserDTO updateUser( @PathVariable("id") Long id, @RequestBody UpdateUserDTO dto );

调用:

java
userFeignClient.updateUser(1L, dto);

10. DELETE 请求

java
@DeleteMapping("/user/{id}") void deleteUser( @PathVariable("id") Long id );

调用:

java
userFeignClient.deleteUser(1L);

11. 请求头 Header

如果接口需要 Token:

java
@GetMapping("/user/info") UserDTO getUserInfo( @RequestHeader("Authorization") String token );

调用:

java
userFeignClient.getUserInfo( "Bearer xxx" );

如果每个接口都要传 Token,不建议每个方法都写 @RequestHeader。

更推荐使用 RequestInterceptor。


12. Feign 请求拦截器

创建配置:

java
@Configuration public class FeignConfig { @Bean public RequestInterceptor requestInterceptor() { return requestTemplate -> { requestTemplate.header( "Authorization", "Bearer xxx" ); }; } }

FeignClient:

java
@FeignClient( name = "user-service", configuration = FeignConfig.class ) public interface UserFeignClient { }

这样每个请求都会自动添加:

http
Authorization: Bearer xxx

适合统一添加:

  • Authorization
  • Token
  • TenantId
  • UserId
  • TraceId
  • ClientId

等请求头。


13. 传递当前请求 Token

微服务中经常需要把当前用户 Token 继续传给下游服务。

例如:

java
@Bean public RequestInterceptor requestInterceptor() { return requestTemplate -> { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) { return; } HttpServletRequest request = attributes.getRequest(); String authorization = request.getHeader("Authorization"); if (authorization != null) { requestTemplate.header( "Authorization", authorization ); } }; }

请求链:

text
前端 ↓ 网关 ↓ 订单服务 ↓ OpenFeign 用户服务

这样用户登录 Token 可以继续传递。


14. 配置超时时间

OpenFeign 调用属于网络请求,因此一定要合理配置超时时间。

例如:

yaml
spring: cloud: openfeign: client: config: default: connectTimeout: 3000 readTimeout: 5000

含义:

text
connectTimeout

连接服务的超时时间。

text
readTimeout

连接成功后等待接口响应的时间。

例如:

text
连接超时:3 秒 读取超时:5 秒

15. 单独配置某个服务

例如:

yaml
spring: cloud: openfeign: client: config: default: connectTimeout: 3000 readTimeout: 5000 user-service: connectTimeout: 2000 readTimeout: 3000

此时:

text
user-service

使用独立配置。

其他服务使用:

text
default

配置。


16. Feign 日志

开发和排查接口调用问题时,可以开启 Feign 日志。

配置日志级别:

java
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }

日志级别:

text
NONE BASIC HEADERS FULL

说明:

日志级别内容
NONE不打印
BASIC方法、URL、状态码、耗时
HEADERSBASIC + 请求头
FULL请求头、请求体、响应头、响应体

开发环境通常可以使用:

java
Logger.Level.FULL

生产环境建议谨慎使用,避免日志过大或者泄露敏感数据。

同时配置:

yaml
logging: level: com.example.feign: DEBUG

否则可能看不到 Feign 日志。


17. 使用 DTO

推荐 Feign 接口不要直接暴露数据库实体 Entity。

例如不要:

java
@GetMapping("/user/{id}") UserEntity getUser();

推荐:

java
@GetMapping("/user/{id}") UserDTO getUser();

服务之间使用:

text
DTO

而不是:

text
Entity

这样可以避免数据库模型变化直接影响服务调用。


18. 统一返回结构

如果系统接口返回:

json
{ "code": 200, "message": "success", "data": { "id": 1, "name": "张三" } }

可以定义:

java
public class Result<T> { private Integer code; private String message; private T data; }

Feign:

java
@GetMapping("/user/{id}") Result<UserDTO> getUser( @PathVariable("id") Long id );

调用:

java
Result<UserDTO> result = userFeignClient.getUser(1L); UserDTO user = result.getData();

19. 服务注册中心调用

例如使用 Nacos。

Nacos 中存在:

text
user-service order-service

订单服务中:

java
@FeignClient(name = "user-service") public interface UserFeignClient { @GetMapping("/user/{id}") UserDTO getUser( @PathVariable("id") Long id ); }

OpenFeign 会通过服务发现找到:

text
user-service

实例。

假设实例有:

text
192.168.1.10:8081 192.168.1.11:8081 192.168.1.12:8081

OpenFeign 会结合 Spring Cloud LoadBalancer 选择一个服务实例。

大致流程:

text
Order Service | | OpenFeign ↓ Service Discovery | ↓ user-service | ↓ LoadBalancer | ├── 192.168.1.10:8081 ├── 192.168.1.11:8081 └── 192.168.1.12:8081

20. OpenFeign 常见项目结构

推荐:

text
src └── main └── java └── com.example │ ├── controller │ ├── service │ ├── dto │ ├── feign │ ├── UserFeignClient.java │ └── OrderFeignClient.java │ ├── config │ └── FeignConfig.java │ └── Application.java

把所有远程接口统一放在:

text
feign

或者:

text
client

目录下。

例如:

text
client/user/UserClient.java client/order/OrderClient.java client/trade/TradeClient.java

大型项目这样更容易维护。


21. FeignClient 命名建议

推荐使用:

text
XxxClient

或者:

text
XxxFeignClient

例如:

java
UserClient OrderClient TradeClient CustomerClient

或者:

java
UserFeignClient OrderFeignClient

项目内部保持统一即可。


22. 接口定义与 FeignClient

假设用户服务:

java
@RestController @RequestMapping("/api/user") public class UserController { @GetMapping("/{id}") public UserDTO getUser( @PathVariable Long id ) { return userService.getUser(id); } }

调用方:

java
@FeignClient( name = "user-service", path = "/api/user" ) public interface UserFeignClient { @GetMapping("/{id}") UserDTO getUser( @PathVariable("id") Long id ); }

调用:

java
@Service public class OrderService { private final UserFeignClient userFeignClient; public OrderService( UserFeignClient userFeignClient ) { this.userFeignClient = userFeignClient; } public void createOrder(Long userId) { UserDTO user = userFeignClient.getUser(userId); // 创建订单 } }

23. 常见错误

错误一:忘记开启 EnableFeignClients

表现:

text
No qualifying bean of type UserFeignClient

解决:

java
@EnableFeignClients

错误二:PathVariable 没有写 name

建议写:

java
@PathVariable("id")

而不是:

java
@PathVariable

例如:

java
@GetMapping("/user/{id}") UserDTO getUser( @PathVariable("id") Long id );

兼容性会更好。


错误三:RequestParam 没写名称

推荐:

java
@RequestParam("page")

例如:

java
@GetMapping("/list") List<UserDTO> list( @RequestParam("page") Integer page );

错误四:服务名称不一致

例如注册中心实际服务:

text
user-service

但代码:

java
@FeignClient(name = "users-service")

会导致找不到服务实例。


错误五:Feign 超时

报错可能出现:

text
Read timed out

或者:

text
Connect timed out

需要检查:

text
网络 服务状态 接口响应速度 Feign 超时时间

错误六:401 / 403

一般检查:

text
Authorization 是否传递 Token 是否正确 网关鉴权 服务内部鉴权

特别是:

text
A 服务 ↓ Feign B 服务

如果 B 服务需要 Token,需要确保 Token 被继续传递。


24. OpenFeign 与 RestTemplate 对比

RestTemplate:

java
restTemplate.getForObject( "http://user-service/user/1", UserDTO.class );

OpenFeign:

java
userClient.getUser(1L);

OpenFeign 的优势:

text
代码更少 接口定义清晰 更容易维护 天然适合微服务 可结合服务发现 可结合负载均衡 支持拦截器 支持统一配置

25. OpenFeign 调用流程

完整流程可以理解为:

text
Controller ↓ Service ↓ FeignClient ↓ OpenFeign ↓ LoadBalancer ↓ 服务注册中心 ↓ 目标服务实例 ↓ HTTP 请求 ↓ Controller ↓ 返回 JSON ↓ Feign Decoder ↓ Java DTO

开发人员看到的代码可能只有:

java
userClient.getUser(1L);

底层实际上完成了一次完整 HTTP 请求。


26. 推荐使用方式

项目中建议遵循以下原则:

  1. FeignClient 只负责远程接口定义。
  2. 不要在 FeignClient 中写业务逻辑。
  3. 请求参数尽量使用 DTO。
  4. 返回值尽量使用 DTO。
  5. 不直接暴露 Entity。
  6. 设置合理的连接和读取超时。
  7. Token、TraceId 等公共 Header 使用拦截器统一处理。
  8. Feign 调用异常需要统一捕获和处理。
  9. 避免服务之间循环调用。
  10. 高频接口调用需要考虑性能、超时和服务稳定性。

27. 一个完整示例

FeignClient

java
@FeignClient( name = "user-service", path = "/api/user", configuration = FeignConfig.class ) public interface UserFeignClient { @GetMapping("/{id}") Result<UserDTO> getUser( @PathVariable("id") Long id ); @GetMapping("/list") Result<List<UserDTO>> list( @RequestParam("page") Integer page, @RequestParam("size") Integer size ); @PostMapping Result<UserDTO> create( @RequestBody CreateUserDTO dto ); }

Service

java
@Service public class OrderService { private final UserFeignClient userFeignClient; public OrderService( UserFeignClient userFeignClient ) { this.userFeignClient = userFeignClient; } public void createOrder(Long userId) { Result<UserDTO> result = userFeignClient.getUser(userId); UserDTO user = result.getData(); System.out.println(user.getName()); } }

28. 一句话理解 OpenFeign

OpenFeign 本质上就是:

text
Java 接口 ↓ 自动转换为 HTTP 请求 ↓ 调用其他服务 REST API

原本需要手动写:

text
URL HTTP 方法 参数 Header JSON 转换 响应解析

OpenFeign 把这些操作封装起来,让微服务调用看起来更像普通 Java 方法调用:

java
userClient.getUser(userId);

因此在 Spring Cloud 微服务项目中,OpenFeign 通常作为服务之间 HTTP 调用的主要方案之一。

Sentinel

image.png

image.png

image.png

image.png

image.png

image.png

本文作者:薛祁

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!