



OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端。
它可以通过定义 Java 接口和注解的方式,直接调用其他服务提供的 HTTP 接口,不需要手动编写 HTTP 请求代码。
例如传统方式可能需要:
javaRestTemplate 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);
}
调用时:
javaUser user = userClient.getUser(1L);
OpenFeign 会自动发送 HTTP 请求。
OpenFeign 一般用于微服务之间的接口调用。
例如:
text订单服务 ↓ 调用用户服务 order-service ↓ OpenFeign user-service
订单服务需要获取用户信息时,可以直接通过 FeignClient 调用用户服务。
适合:
Spring Boot / Spring Cloud 项目中引入:
xml<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
一般建议 Spring Boot 与 Spring Cloud 使用对应的兼容版本。
在 Spring Boot 启动类中添加:
java@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
核心注解:
java@EnableFeignClients
作用:
扫描项目中的 @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);
}
}
基本写法:
java@FeignClient(name = "user-service")
表示服务名称。
例如:
java@FeignClient(name = "user-service")
如果项目接入 Nacos、Eureka 等注册中心:
textuser-service
会对应注册中心里的服务名称。
OpenFeign 会根据服务名找到对应服务实例。
如果不是通过注册中心调用,而是直接调用固定地址,可以使用:
java@FeignClient(
name = "userClient",
url = "http://localhost:8081"
)
例如:
java@FeignClient(
name = "weatherClient",
url = "https://api.example.com"
)
public interface WeatherClient {
@GetMapping("/weather")
String getWeather();
}
可以统一配置接口前缀:
java@FeignClient(
name = "user-service",
path = "/api/user"
)
public interface UserFeignClient {
@GetMapping("/{id}")
UserDTO getUser(@PathVariable("id") Long id);
}
最终请求:
text/api/user/1
可以为指定 FeignClient 配置单独的配置类:
java@FeignClient(
name = "user-service",
configuration = FeignConfig.class
)
例如配置:
接口:
httpGET /user/1001
Feign:
java@GetMapping("/user/{id}")
UserDTO getUser(
@PathVariable("id") Long id
);
调用:
javauserFeignClient.getUser(1001L);
接口:
httpGET /user/list?page=1&size=20
Feign:
java@GetMapping("/user/list")
List<UserDTO> listUser(
@RequestParam("page") Integer page,
@RequestParam("size") Integer size
);
调用:
javauserFeignClient.listUser(1, 20);
接口:
httpPOST /user Content-Type: application/json
请求体:
json{
"name": "张三",
"age": 20
}
Feign:
java@PostMapping("/user")
UserDTO createUser(
@RequestBody CreateUserDTO dto
);
调用:
javaCreateUserDTO dto = new CreateUserDTO();
dto.setName("张三");
dto.setAge(20);
UserDTO user = userFeignClient.createUser(dto);
java@PutMapping("/user/{id}")
UserDTO updateUser(
@PathVariable("id") Long id,
@RequestBody UpdateUserDTO dto
);
调用:
javauserFeignClient.updateUser(1L, dto);
java@DeleteMapping("/user/{id}")
void deleteUser(
@PathVariable("id") Long id
);
调用:
javauserFeignClient.deleteUser(1L);
如果接口需要 Token:
java@GetMapping("/user/info")
UserDTO getUserInfo(
@RequestHeader("Authorization") String token
);
调用:
javauserFeignClient.getUserInfo(
"Bearer xxx"
);
如果每个接口都要传 Token,不建议每个方法都写 @RequestHeader。
更推荐使用 RequestInterceptor。
创建配置:
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 {
}
这样每个请求都会自动添加:
httpAuthorization: Bearer xxx
适合统一添加:
等请求头。
微服务中经常需要把当前用户 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 可以继续传递。
OpenFeign 调用属于网络请求,因此一定要合理配置超时时间。
例如:
yamlspring:
cloud:
openfeign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 5000
含义:
textconnectTimeout
连接服务的超时时间。
textreadTimeout
连接成功后等待接口响应的时间。
例如:
text连接超时:3 秒 读取超时:5 秒
例如:
yamlspring:
cloud:
openfeign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 5000
user-service:
connectTimeout: 2000
readTimeout: 3000
此时:
textuser-service
使用独立配置。
其他服务使用:
textdefault
配置。
开发和排查接口调用问题时,可以开启 Feign 日志。
配置日志级别:
java@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
日志级别:
textNONE BASIC HEADERS FULL
说明:
| 日志级别 | 内容 |
|---|---|
| NONE | 不打印 |
| BASIC | 方法、URL、状态码、耗时 |
| HEADERS | BASIC + 请求头 |
| FULL | 请求头、请求体、响应头、响应体 |
开发环境通常可以使用:
javaLogger.Level.FULL
生产环境建议谨慎使用,避免日志过大或者泄露敏感数据。
同时配置:
yamllogging:
level:
com.example.feign: DEBUG
否则可能看不到 Feign 日志。
推荐 Feign 接口不要直接暴露数据库实体 Entity。
例如不要:
java@GetMapping("/user/{id}")
UserEntity getUser();
推荐:
java@GetMapping("/user/{id}")
UserDTO getUser();
服务之间使用:
textDTO
而不是:
textEntity
这样可以避免数据库模型变化直接影响服务调用。
如果系统接口返回:
json{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "张三"
}
}
可以定义:
javapublic class Result<T> {
private Integer code;
private String message;
private T data;
}
Feign:
java@GetMapping("/user/{id}")
Result<UserDTO> getUser(
@PathVariable("id") Long id
);
调用:
javaResult<UserDTO> result =
userFeignClient.getUser(1L);
UserDTO user = result.getData();
例如使用 Nacos。
Nacos 中存在:
textuser-service order-service
订单服务中:
java@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/user/{id}")
UserDTO getUser(
@PathVariable("id") Long id
);
}
OpenFeign 会通过服务发现找到:
textuser-service
实例。
假设实例有:
text192.168.1.10:8081 192.168.1.11:8081 192.168.1.12:8081
OpenFeign 会结合 Spring Cloud LoadBalancer 选择一个服务实例。
大致流程:
textOrder Service | | OpenFeign ↓ Service Discovery | ↓ user-service | ↓ LoadBalancer | ├── 192.168.1.10:8081 ├── 192.168.1.11:8081 └── 192.168.1.12:8081
推荐:
textsrc └── main └── java └── com.example │ ├── controller │ ├── service │ ├── dto │ ├── feign │ ├── UserFeignClient.java │ └── OrderFeignClient.java │ ├── config │ └── FeignConfig.java │ └── Application.java
把所有远程接口统一放在:
textfeign
或者:
textclient
目录下。
例如:
textclient/user/UserClient.java client/order/OrderClient.java client/trade/TradeClient.java
大型项目这样更容易维护。
推荐使用:
textXxxClient
或者:
textXxxFeignClient
例如:
javaUserClient OrderClient TradeClient CustomerClient
或者:
javaUserFeignClient OrderFeignClient
项目内部保持统一即可。
假设用户服务:
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);
// 创建订单
}
}
表现:
textNo qualifying bean of type UserFeignClient
解决:
java@EnableFeignClients
建议写:
java@PathVariable("id")
而不是:
java@PathVariable
例如:
java@GetMapping("/user/{id}")
UserDTO getUser(
@PathVariable("id") Long id
);
兼容性会更好。
推荐:
java@RequestParam("page")
例如:
java@GetMapping("/list")
List<UserDTO> list(
@RequestParam("page") Integer page
);
例如注册中心实际服务:
textuser-service
但代码:
java@FeignClient(name = "users-service")
会导致找不到服务实例。
报错可能出现:
textRead timed out
或者:
textConnect timed out
需要检查:
text网络 服务状态 接口响应速度 Feign 超时时间
一般检查:
textAuthorization 是否传递 Token 是否正确 网关鉴权 服务内部鉴权
特别是:
textA 服务 ↓ Feign B 服务
如果 B 服务需要 Token,需要确保 Token 被继续传递。
RestTemplate:
javarestTemplate.getForObject(
"http://user-service/user/1",
UserDTO.class
);
OpenFeign:
javauserClient.getUser(1L);
OpenFeign 的优势:
text代码更少 接口定义清晰 更容易维护 天然适合微服务 可结合服务发现 可结合负载均衡 支持拦截器 支持统一配置
完整流程可以理解为:
textController ↓ Service ↓ FeignClient ↓ OpenFeign ↓ LoadBalancer ↓ 服务注册中心 ↓ 目标服务实例 ↓ HTTP 请求 ↓ Controller ↓ 返回 JSON ↓ Feign Decoder ↓ Java DTO
开发人员看到的代码可能只有:
javauserClient.getUser(1L);
底层实际上完成了一次完整 HTTP 请求。
项目中建议遵循以下原则:
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
);
}
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());
}
}
OpenFeign 本质上就是:
textJava 接口 ↓ 自动转换为 HTTP 请求 ↓ 调用其他服务 REST API
原本需要手动写:
textURL HTTP 方法 参数 Header JSON 转换 响应解析
OpenFeign 把这些操作封装起来,让微服务调用看起来更像普通 Java 方法调用:
javauserClient.getUser(userId);
因此在 Spring Cloud 微服务项目中,OpenFeign 通常作为服务之间 HTTP 调用的主要方案之一。






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