Spring WebClient vs. RestTemplate

1. 简介

本教程中,咱们将对比 Spring 的两种 Web 客户端实现 —— RestTemplate 和 Spring 5 中全新的 Reactive 替代方案 WebClientjava

2. 阻塞式 vs 非阻塞式客户端

Web 应用中,对其余服务进行 HTTP 调用是一个很常见的需求。所以,咱们须要一个 Web 客户端工具。react

2.1. RestTemplate 阻塞式客户端

很长一段时间以来,Spring 一直提供 RestTemplate 做为 Web 客户端抽象。在底层,RestTemplate 使用了基于每一个请求对应一个线程模型(thread-per-request)的 Java Servlet API。git

这意味着,直到 Web 客户端收到响应以前,线程都将一直被阻塞下去。而阻塞代码带来的问题则是,每一个线程都消耗了必定的内存和 CPU 周期。github

让咱们考虑下有不少传入请求,它们正在等待产生结果所需的一些慢服务。web

等待结果的请求早晚都会堆积起来。**所以,程序将建立不少线程,这些线程将耗尽线程池或占用全部可用内存。**因为频繁的 CPU 上下文(线程)切换,咱们还会遇到性能降低的问题。spring

2.2. WebClient 非阻塞式客户端

另外一方面,WebClient 使用 Spring Reactive Framework 所提供的异步非阻塞解决方案。浏览器

RestTemplate 为每一个事件(HTTP 请求)建立一个新的 线程 时,WebClient 将为每一个事件建立相似于“任务”的东东。幕后,Reactive 框架将对这些 “任务” 进行排队,并仅在适当的响应可用时执行它们。bash

Reactive 框架使用事件驱动的体系结构。它提供了经过 Reactive Streams API 组合异步逻辑的方法。所以,与同步/阻塞方法相比,Reactive 可使用更少的线程和系统资源来处理更多的逻辑。多线程

WebClientSpring WebFlux 库的一部分。所以,咱们还可使用流畅的函数式 API 编写客户端代码,并将响应类型(Mono 和 Flux)做为声明来进行组合。并发

3. 案例对比

为了演示两种方法间的差别,咱们须要使用许多并发客户端请求来运行性能测试。在必定数量的并发请求后,咱们将看到阻塞方法性能的显著降低。

另外一方面,不管请求数量如何,反应式/非阻塞方法均可以提供恒定的性能。

就本文而言,**让咱们实现两个 REST 端点,一个使用 RestTemplate,另外一个使用 WebClient。**他们的任务是调用另外一个响应慢的 REST Web 服务,该服务返回一个 Tweet List。

首先,咱们须要引入 Spring Boot WebFlux starter 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
复制代码

接下来,这是咱们的慢服务 REST 端点:

@GetMapping("/slow-service-tweets")
private List<Tweet> getAllTweets() {
    Thread.sleep(2000L); // delay
    return Arrays.asList(
      new Tweet("RestTemplate rules", "@user1"),
      new Tweet("WebClient is better", "@user2"),
      new Tweet("OK, both are useful", "@user1"));
}
复制代码

3.1. 使用 RestTemplate 调用慢服务

如今,让咱们来实现另外一个 REST 端点,它将经过 Web 客户端调用咱们的慢服务。

首先,咱们来使用 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;
}
复制代码

当咱们调用这个端点时,因为 RestTemplate 的同步特性,代码将会阻塞以等待来自慢服务的响应。只有当收到响应后,才会执行此方法中的其他代码。经过日志,咱们能够看到:

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.2. 使用 WebClient 调用慢服务

其次,让咱们使用 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;
}
复制代码

本例中,WebClient 返回一个 Flux 生产者后完成方法的执行。一旦结果可用,发布者将开始向其订阅者发送 tweets。注意,调用 /tweets-non-blocking 这个端点的客户端(本例中的 Web 浏览器)也将订阅返回的 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. 结论

本文中,咱们探讨了在 Spring 中使用 Web 客户端的两种不一样方式。

RestTemplate 使用 Java Servlet API,所以是同步和阻塞的。相反,WebClient 是异步的,在等待响应返回时不会阻塞正在执行的线程。只有当程序就绪时,才会产生通知。

RestTemplate 仍将会被使用。但在某些状况下,与阻塞方法相比,非阻塞方法使用的系统资源要少得多。所以,在这些状况下,WebClient 不失为是更好的选择。

文中提到的全部代码片断,都可在 GitHub 上找到。

原文:www.baeldung.com/spring-webc…

做者:Drazen Nikolic

译者:万想


送福利啦~ 近期将以前已翻译文章,整理成了PDF。 ​ 在公众号后台回复:002便可领取哦~ ​ 后续也会不断更新PDF的内容,敬请期待!

img
相关文章
相关标签/搜索