1. 概述

在实际开发中,我们常常需要调用外部服务接口。Spring 提供了两个常用的客户端工具:RestTemplateWebClient

  • RestTemplate 是传统的同步阻塞式客户端
  • WebClient 是 Spring 5 引入的响应式非阻塞客户端

本文将从原理、使用方式和性能表现等方面对两者进行对比,帮助你在不同场景下选择合适的客户端工具。


2. 阻塞 vs 非阻塞客户端

2.1. RestTemplate:阻塞式客户端

RestTemplate 是 Spring 长期支持的 HTTP 客户端,底层依赖 Java Servlet API,采用经典的线程模型 —— 每个请求都会占用一个线程,直到响应返回。

✅ 优点:简单易用,API 稳定
❌ 缺点:每个请求都阻塞线程,高并发下容易造成线程堆积,资源耗尽

举个例子:假设某个服务响应较慢,大量请求堆积等待,系统不得不创建大量线程处理这些请求,最终可能导致线程池耗尽或内存溢出。

2.2. WebClient:非阻塞式客户端

WebClient 是 Spring 5 引入的响应式客户端,基于 Reactor 框架,采用事件驱动模型,支持异步、非阻塞调用。

⚠️ 它不依赖线程等待响应,而是通过回调或响应式流(如 MonoFlux)来处理结果。

✅ 优点:

  • 高并发场景下资源利用率更高
  • 支持响应式编程风格
  • 可组合多个异步操作,逻辑更清晰

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 学习。


原始标题:Spring WebClient vs. RestTemplate