1. WebClient 简介
1.1 什么是 WebClient
-
WebClient 是 Spring 5 引入的响应式 HTTP 客户端,基于 Reactor 实现非阻塞 I/O。支持异步、非阻塞式调用,适用于微服务、API 网关等高并发场景。它是 RestTemplate 的响应式版,能更好地支持响应式编程和流式数据处理。
-
核心特性:
-
非阻塞、异步 HTTP 请求
-
支持
Mono
(0..1)和Flux
(0..N)响应流 -
内置 JSON、XML、Form 编解码器
-
Filter 链机制支持拦截、认证、日志等
-
1.2 WebClient 与 RestTemplate 区别
特性 | RestTemplate | WebClient |
---|---|---|
阻塞 | 是 | 否(非阻塞) |
响应式 | 否 | 是(Mono/Flux) |
并发 | 线程池控制 | 背压机制 + 连接池 |
SSE/流式 | 不支持 | 支持 |
编码/解码 | 手动/自动 | 自动支持 JSON/XML/Form |
1.3 适用场景
-
微服务之间非阻塞调用
-
高并发 HTTP 请求
-
流式数据/Server-Sent Events (SSE)
-
响应式编程体系(WebFlux、RSocket)
2. 快速开始
2.1 依赖引入
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-webflux'
2.2 创建 WebClient 实例
// 简单创建
WebClient client = WebClient.create("https://api.example.com");
// Builder 创建
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
2.3 基本 GET/POST 请求示例
java
// GET 请求
Mono<String> response = client.get()
.uri("/users/{id}", 1)
.retrieve()
.bodyToMono(String.class);
// POST 请求
Mono<String> postResponse = client.post()
.uri("/users")
.bodyValue(new User("Alice", 20))
.retrieve()
.bodyToMono(String.class);
3. 请求构建详解
3.1 URI 构建与参数传递
java
// Path Variable
client.get().uri("/users/{id}", 123).retrieve().bodyToMono(User.class);
// Query 参数
client.get().uri(uriBuilder -> uriBuilder.path("/users")
.queryParam("page", 1)
.queryParam("size", 10)
.build())
.retrieve()
.bodyToMono(UserList.class);
3.2 Header 设置
java
client.get()
.uri("/users/1")
.header("Authorization", "Bearer token")
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(User.class);
3.3 Body 构建
- JSON
java
client.post()
.uri("/users")
.bodyValue(new User("Alice", 20))
.retrieve()
.bodyToMono(String.class);
- Form
java
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("username", "alice");
formData.add("password", "123456");
client.post()
.uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.bodyValue(formData)
.retrieve()
.bodyToMono(String.class);
- 文件上传
java
MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
data.add("file", new FileSystemResource("test.txt"));
client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(data))
.retrieve()
.bodyToMono(String.class);
4. 响应处理
4.1 Mono 与 Flux 基础
-
Mono<T>
→ 0..1 个元素 -
Flux<T>
→ 0..N 个元素(流式响应)
4.2 bodyToMono 与 bodyToFlux
java
Mono<User> monoUser = client.get().uri("/user/1").retrieve().bodyToMono(User.class);
Flux<User> fluxUsers = client.get().uri("/users").retrieve().bodyToFlux(User.class);
5. 错误与异常处理
5.1 onStatus 用法
java
client.get()
.uri("/users/999")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, resp -> Mono.error(new RuntimeException("Client Error")))
.onStatus(HttpStatus::is5xxServerError, resp -> Mono.error(new RuntimeException("Server Error")))
.bodyToMono(User.class);
5.2 doOnError、retry 等响应式异常处理
java
client.get()
.uri("/slow-api")
.retrieve()
.bodyToMono(String.class)
.doOnError(e -> log.error("Request failed", e))
.retry(3)
.timeout(Duration.ofSeconds(2));
6. 高级用法
6.1 连接池与超时配置
java
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxConnections(50)
.maxIdleTime(Duration.ofSeconds(30))
.build();
HttpClient httpClient = HttpClient.create(provider)
.responseTimeout(Duration.ofSeconds(5));
WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
6.2 拦截器与过滤器(ExchangeFilterFunction)
java
WebClient.builder()
.filter((request, next) -> {
System.out.println("Request: " + request.method() + " " + request.url());
return next.exchange(request);
})
.build();
6.3 OAuth2 / 认证集成
java
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
WebClient client = WebClient.builder()
.apply(oauth.oauth2Configuration())
.build();
6.4 SSE / 流式响应处理
java
Flux<ServerSentEvent<String>> events = client.get()
.uri("/sse/stream")
.retrieve()
.bodyToFlux(new ParameterizedTypeReference<ServerSentEvent<String>>() {});
events.subscribe(e -> System.out.println(e.data()));
7. 性能优化与最佳实践
-
单例复用 WebClient,避免重复创建连接池
-
取消订阅 / 资源释放 :
Disposable.dispose()
-
大文件 / 流式下载 :
bodyToFlux(DataBuffer.class)
配合DataBufferUtils.write
-
日志与调试技巧:HttpClient.create().wiretap(true) // Netty 级别日志
8. 测试与 Mock
8.1 单元测试(StepVerifier)
java
Mono<User> monoUser = client.get().uri("/user/1").retrieve().bodyToMono(User.class);
StepVerifier.create(monoUser)
.expectNextMatches(user -> user.getName().equals("Alice"))
.verifyComplete();
8.2 MockWebServer 使用
-
可用
okhttp3.mockwebserver.MockWebServer
模拟 HTTP 服务 -
配合 WebClient 测试请求、响应和异常
8.3 WebTestClient
webTestClient.get().uri("/users/1")
.exchange()
.expectStatus().isOk()
.expectBody(User.class)
.value(user -> assertThat(user.getName()).isEqualTo("Tom"));
9. 常见问题与解决方案
-
超时问题 → 配置
responseTimeout
或.timeout(Duration)
-
连接数限制 → 调整
ConnectionProvider.maxConnections
-
兼容性注意事项 → WebClient 默认 Reactor Netty,需确保服务端支持 HTTP/1.1 或 HTTP/2
10. 实战案例
10.1 与 Spring Cloud 集成
java
@Autowired
private WebClient.Builder webClientBuilder;
Mono<String> result = webClientBuilder.build()
.get()
.uri("http://user-service/users/1")
.retrieve()
.bodyToMono(String.class);
10.2 微服务间调用
-
WebClient + Ribbon / LoadBalancerClient 或 Spring Cloud LoadBalancer
-
支持异步并发请求、聚合结果