1. 概述
在实际开发中,我们常常需要调用外部服务接口。Spring 提供了两个常用的客户端工具:RestTemplate
和 WebClient
。
RestTemplate
是传统的同步阻塞式客户端WebClient
是 Spring 5 引入的响应式非阻塞客户端
本文将从原理、使用方式和性能表现等方面对两者进行对比,帮助你在不同场景下选择合适的客户端工具。
2. 阻塞 vs 非阻塞客户端
2.1. RestTemplate:阻塞式客户端
RestTemplate
是 Spring 长期支持的 HTTP 客户端,底层依赖 Java Servlet API,采用经典的线程模型 —— 每个请求都会占用一个线程,直到响应返回。
✅ 优点:简单易用,API 稳定
❌ 缺点:每个请求都阻塞线程,高并发下容易造成线程堆积,资源耗尽
举个例子:假设某个服务响应较慢,大量请求堆积等待,系统不得不创建大量线程处理这些请求,最终可能导致线程池耗尽或内存溢出。
2.2. WebClient:非阻塞式客户端
WebClient
是 Spring 5 引入的响应式客户端,基于 Reactor 框架,采用事件驱动模型,支持异步、非阻塞调用。
⚠️ 它不依赖线程等待响应,而是通过回调或响应式流(如 Mono
和 Flux
)来处理结果。
✅ 优点:
- 高并发场景下资源利用率更高
- 支持响应式编程风格
- 可组合多个异步操作,逻辑更清晰
3. 实战对比
为了直观展示两者的差异,我们模拟一个调用慢服务的场景。
3.1. 模拟慢服务接口
我们先定义一个返回 tweets 列表的慢服务接口:
@GetMapping("/slow-service-tweets")
private List<Tweet> getAllTweets() {
Thread.sleep(2000L); // 模拟延迟
return Arrays.asList(
new Tweet("RestTemplate rules", "@user1"),
new Tweet("WebClient is better", "@user2"),
new Tweet("OK, both are useful", "@user1"));
}
该接口故意延迟 2 秒后返回结果,用于测试客户端的阻塞行为。
3.2. 使用 RestTemplate 调用慢服务
@GetMapping("/tweets-blocking")
public List<Tweet> getTweetsBlocking() {
log.info("Starting BLOCKING Controller!");
final String uri = getSlowServiceUri();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Tweet>> response = restTemplate.exchange(
uri, HttpMethod.GET, null,
new ParameterizedTypeReference<List<Tweet>>() {});
List<Tweet> result = response.getBody();
result.forEach(tweet -> log.info(tweet.toString()));
log.info("Exiting BLOCKING Controller!");
return result;
}
调用这个接口时,主线程会阻塞直到响应返回,日志输出如下:
Starting BLOCKING Controller!
Tweet(text=RestTemplate rules, username=@user1)
Tweet(text=WebClient is better, username=@user2)
Tweet(text=OK, both are useful, username=@user1)
Exiting BLOCKING Controller!
3.3. 使用 WebClient 调用慢服务
@GetMapping(value = "/tweets-non-blocking",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Tweet> getTweetsNonBlocking() {
log.info("Starting NON-BLOCKING Controller!");
Flux<Tweet> tweetFlux = WebClient.create()
.get()
.uri(getSlowServiceUri())
.retrieve()
.bodyToFlux(Tweet.class);
tweetFlux.subscribe(tweet -> log.info(tweet.toString()));
log.info("Exiting NON-BLOCKING Controller!");
return tweetFlux;
}
该接口立即返回,当响应可用时通过 Flux
流式推送结果。日志输出如下:
Starting NON-BLOCKING Controller!
Exiting NON-BLOCKING Controller!
Tweet(text=RestTemplate rules, username=@user1)
Tweet(text=WebClient is better, username=@user2)
Tweet(text=OK, both are useful, username=@user1)
可以看到,控制器方法在响应返回前就已结束执行,真正做到了“非阻塞”。
4. 总结
特性 | RestTemplate | WebClient |
---|---|---|
是否阻塞 | ✅ 阻塞 | ❌ 非阻塞 |
是否响应式 | ❌ 否 | ✅ 是 |
线程模型 | 每请求一线程 | 事件驱动、少量线程 |
API 稳定性 | ✅ 稳定 | ✅ 稳定(Spring 5+) |
适用场景 | 简单调用、旧项目 | 高并发、响应式架构 |
✅ 推荐使用 WebClient 的场景:
- 接口调用频繁且响应时间长
- 构建响应式服务或微服务
- 需要异步组合多个请求结果
⚠️ 注意:
- 如果你的项目不使用响应式编程栈(如 WebFlux),继续使用
RestTemplate
也没问题 RestTemplate
并未被弃用,但官方推荐新项目使用WebClient
所有代码示例已整理在 GitHub 仓库 中,欢迎 clone 学习。