Spring WebFlux 与 WebClient 使用指南及最佳实践
目录
- Spring WebFlux 与 WebClient 使用指南
- 1. WebClient 概述
- 2. 核心依赖配置
- 3. WebClient 的创建与配置
- 3.1 全局配置(推荐)
- 3.2 临时创建(按需使用)
- 4. 发送 HTTP 请求
- 4.1 GET 请求(携带 Token)
- 4.2 POST 请求(发送数组 Body)
- 5. 错误处理
- 5.1 HTTP 错误处理(onStatus)
- 5.2 非 HTTP 错误处理(doOnError)
- 6. 同步与异步调用
- 6.1 异步调用(subscribe)
- 6.2 同步调用(block,仅用于测试或特殊场景)
- 7. 统一响应结构
- 7.1 定义统一响应类
- 7.2 转换响应
- 8. 日志与监控
- 8.1 成功日志
- 8.2 错误日志
- 9. 高级配置
- 9.1 超时与重试
- 9.2 连接池配置
- 10. 常见问题与最佳实践
- 10.1 避免手动调用subscribe
- 10.2 统一异常处理
- 10.3 性能优化
- 附录:完整代码示例
- 发送 POST 请求并处理错误
Spring WebFlux 与 WebClient 使用指南
1. WebClient 概述
WebClient 是 Spring WebFlux 模块提供的非阻塞、响应式 HTTP 客户端,基于 Project Reactor 实现,适用于高并发场景。
核心优势:- 支持异步非阻塞 I/O,提升吞吐量。
- 链式 API 设计,便于组合操作。
- 集成 Spring 生态,支持自动编解码(jsON、XML)。
2. 核心依赖配置
在 pom.xml 中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
3. WebClient 的创建与配置
3.1 全局配置(推荐)
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("https://api.example.com") // 基础 URL
.defaultHeader("Accept", "application/json")
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.responseTimeout(Duration.ofSeconds(30)) // 响应超时
)
.build();
}
}
3.2 临时创建(按需使用)
WebClient client = WebClient.create(www.devze.com"https://api.example.com");
4. 发送 HTTP 请求
4.1 GET 请求(携带 Token)
public Mono<User> getUser(String id, String token) {
return webClient.get()
.uri("/users/{id}", id) // 路径参数
.header("Token-Test", token) // 自定义 Token
.retrieve()
.bodyToMono(User.class); // 解析为对象
}
4.2 POST 请求(发送数组 Body)
public Mono<String> postUsers(List<User> users, String token) {
return webClient.post()
.uri("/users/BATch")
.header("Token-Test", token)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(users) // 发送 List 集合
.retrieve()
.bodyToMono(String.class);
}
5. 错误处理
5.1 HTTP 错误处理(onStatus)
.onStatus(HttpStatus::isError, response ->
response.bodyToMono(String.class)
.flatMap(errorBody -> {
String msg = String.format("状态码: %d, 错误信息: %s",
response.rawStatusCode(), errorBody);
log.error(msg);
return Mono.error(new ServiceException(msg));
})
)
5.2 非 HTTP 错误处理(doOnError)
.doOnError(error -> {
if (!(error instanceof ServiceException)) {
log.error("非 HTTP 错误: {}", error.getMessage());
}
})
6. 同步与异步调用
6.1 异步调用(subscribe)
webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class)
.subscribe(
data -> log.info("成功: {}", data),
error -> log.error("失败: {}", error)
);
6.2 同步调用(block,仅用于测试或特殊场景)
try {
String result = webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofSeconds(10)); // 阻塞等待
} catch (Exception e) {
log.error("请求失败", e);
}
7. 统一响应结构
7.1 定义统一响应类
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "Success", data);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
7.2 转换响应
public Mono<ApiResponse<User>> getUser(String id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
.map(ApiResponse::success) //编程客栈 包装为成功响应
.onErrorResume(e ->
Mono.just(ApiResponse.error(500, e.getMessage()))
);
}
8. 日志与监控
8.1 成功日志
.doOnSuccess(response ->
log.info("请求成功: {}", response)
)
8.2 错误日志
.doOnError(error ->
log.error("请求失败: {}", error.getMessage())
)
9. 高级配置
9.1 超时与重试
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.responseTimeout(Duration.ofSeconds(30)) // 响应超时
)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))) // 指数退避重试
9.2 连接池配置
HttpClient.create()
.baseUrl("https://api.example.com")
.tcpConfiguration(tcpClient ->
tcpClient.option(Channhttp://www.devze.comelOption.CONNECT_TIMEOUT_MILLIS, 5000)
)
10. 常见问题与最佳实践
10.1 避免手动调用subscribe
- 错误示例:
// Service 层中手动调用 subscribe(不推荐) public void sendData() { webClient.post().subscribe(); // 可能导编程客栈致资源泄漏 } - 正确做法:在 Controller 或调用方返回
Mono/Flux,由框架处理订阅。
10.2 统一异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<ApiResponse<?>> handleServiceException(ServiceException e) {
return Mono.just(ApiResponse.error(500, e.getMessage()));
}
}
10.3 性能优化
- 复用 WebClient 实例:避免频繁创建新实例。
- 合理设置超时:根据接口 SLA 调整响应和连接超时。
附录:完整代码示例
发送 POST 请求并处理错误
public Mono<ApiResponse<String>> syncData(List<User> users, String token) {
String uri = UriComponentsBuilder.fromUriString("https://api.example.com")
.path("/batch")
.queryParam("source", "web")
.queryParamIfPresent("type", Optional.ofNullable("test".equals(activeProfile) ? "test" : null))
.build()
.toUriString();
return webClient.post()
.uri(uri)
.header("Token-Test", token)
.headers(headers -> {
if ("test".equals(activeProfile)) {
headers.add("type", "test"); // 仅测试环境添加
}
})
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(users)
.retrieve()
.onStatus(HttpStatus::isError, response ->
response.bodyToMono(String.class)
.flatMap(error -> {
String msg = String.format("状态码:%d, 错误信息: %s", response.rawStatusCode(), error);
log.error(msg);
return Mono.error(new RuntimeException(msg));
})
)
.bodyToMono(ApiResponse.class) // 解析为自定义响应对象
// 检查业务状态码
.flatMap(result -> {
if (result.getStatusCode() != 200) {
String msg = String.format("业务错误: code=%d, message=%s",
result.getStatusCode(), result.getMessage());
log.error(msg);
return Mono.error(new ServiceException(msg));
}
return Mono.just(result);
})
// 成功记录日志
.doOnSuccess(success -> log.info("请求成功"))
// 失败记录日志
.doOnError(error -> log.error("失败", error.getMessage()编程))
.onErrorResume(e ->
Mono.just(ApiResponse.error(500, e.getMessage()))
);
}
通过本文档,您可全面掌握 WebClient 的核心用法、错误处理策略及性能优化技巧。建议结合项目需求灵活调整配置,遵循响应式编程最佳实践。
到此这篇关于Spring WebFlux 与 WebClient 使用指南及最佳实践的文章就介绍到这了,更多相关Spring WebFlux WebClient 使用内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论