背景
本文是《Java 后端从小白到大神》修仙系列之框架学习,Spring Cloud 微服务实战系列的 第四篇
。Spring Cloud 是构建微服务架构的基石,拥有完整的服务治理生态,在云原生架构中广泛应用。本系列将从架构认知到实际工程,逐步构建一套企业级 Spring Cloud 微服务项目。若想详细学习请点击首篇博文开始,现在开始学习。
文章概览
OpenFeign 深度实战与容错机制构建:
- OpenFeign 声明式调用原理
- 复杂参数传递、多文件上传实践
- Feign 超时、重试、日志配置
- 容错降级:Fallback 结合 Sentinel/Resilience4j
- Feign 请求上下文透传(Token、Header)
1. OpenFeign 简介与工作原理
OpenFeign 是 Spring Cloud 官方推荐的远程调用方式,支持:
- 声明式接口 定义
- Ribbon/LoadBalancer 自动负载均衡
- 与 Sentinel / Resilience4j 兼容的容错机制
- 内置重试和日志功能
核心原理:
1
|
@FeignClient → 动态代理 → 发送 HTTP 请求 → 获取远程服务结果
|
项目模块依赖关系图
1
2
|
user-service ⇦⇨ user-api(定义 Feign 接口与 DTO)
task-service ⇨ 依赖 user-api,实现用户相关远程调用
|
配置与依赖
user-api 中定义接口和 DTO
user-api/pom.xml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Feign 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 通用 DTO / JSON 支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
|
UserClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package com.yutao.feign;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import com.yutao.dto.UserDTO;
@FeignClient(
name = "user-service",
path = "/user", // 网关 StripPrefix 已配置为1
contextId = "userClient"
)
public interface UserClient {
@GetMapping("/get/{id}")
UserDTO getUserById(@PathVariable("id") Long id);
@PostMapping("/save")
Boolean save(@RequestBody UserDTO user);
@GetMapping("/list")
List<UserDTO> listUsers();
// 示例:传多个参数
@GetMapping("/check")
Boolean checkUsername(@RequestParam("username") String username, @RequestParam("email") String email);
// 示例:文件上传
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String uploadAvatar(@RequestPart("file") org.springframework.web.multipart.MultipartFile file);
}
|
UserDTO.java
1
2
3
4
5
6
7
8
9
10
|
package com.yutao.dto;
import lombok.Data;
@Data
public class UserDTO {
private Long id;
private String username;
private String email;
}
|
task-service 引入 user-api
task-service/pom.xml
1
2
3
4
|
<dependency>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
</dependency>
|
TaskServiceApplication.java
1
2
3
|
@EnableFeignClients(basePackages = "com.yutao.userapi")
@SpringBootApplication
public class TaskServiceApplication { ... }
|
实战示例:任务服务远程调用用户服务
TaskController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@RestController
@RequestMapping("/tasks")
@RequiredArgsConstructor
public class TaskController {
private final UserClient userClient;
@GetMapping("/assigned")
public ResponseEntity<String> assignTask(@RequestParam Long userId) {
UserDTO user = userClient.getUserInfo(userId);
return ResponseEntity.ok("任务已分配给用户:" + user.getUsername());
}
}
|
2. 参数传递实战(含文件上传)
GET + 对象参数(使用 @SpringQueryMap)
1
2
|
@GetMapping("/query")
UserDTO queryUser(@SpringQueryMap UserQueryDTO query);
|
文件上传(单文件)
1
2
|
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String uploadFile(@RequestPart("file") MultipartFile file);
|
3. Feign 配置:超时 / 重试 / 日志
这段配置放在 Feign 客户端调用方(例如:task-service)的 application.yaml 中
1
2
3
4
5
6
7
8
9
10
11
|
feign:
client:
config:
default:
connectTimeout: 2000
readTimeout: 3000
loggerLevel: FULL
retryer:
maxAttempts: 3
period: 100
maxPeriod: 1000
|
可按客户端微调:如 feign.client.config.user-service.xxx
4. 容错降级:结合 Resilience4j 使用 fallback
这段配置属于 Feign 客户端调用方(例如:task-service)的中
添加依赖:
1
2
3
4
|
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
|
使用 fallback:
1
2
3
4
5
6
7
8
9
10
|
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient { ... }
@Component
public class UserClientFallback implements UserClient {
@Override
public UserDTO getUserInfo(Long userId) {
return new UserDTO(-1L, "默认用户");
}
}
|
定义 Feign 拦截器,这段配置属于 Feign 客户端调用方(例如:task-service)的中
1
2
3
4
5
6
7
8
9
10
11
|
@Configuration
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String token = RequestContextHolder.getRequestAttributes()
.getAttribute("token", RequestAttributes.SCOPE_REQUEST);
if (token != null) {
template.header("Authorization", token.toString());
}
}
}
|
结合 gateway-service
传递 Token 到下游服务。
总结
技术点 |
实践总结 |
Feign 模块拆分 |
Feign 接口应集中在 user-api ,服务使用时引入 |
超时/重试配置 |
使用 feign.client.config 做全局/单个微调 |
容错策略 |
推荐使用 Resilience4j fallback |
Header 透传 |
配合 gateway 使用 RequestInterceptor |