Spring WebClient vs. RestTemplate – Spring WebClient vs. RestTemplate

最后修改: 2019年 7月 22日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to compare two of Spring’s web client implementations — RestTemplate and new Spring 5’s reactive alternative WebClient.

在本教程中,我们将比较Spring的两个Web客户端实现–RestTemplate和Spring 5的新反应式替代方案WebClient

2. Blocking vs Non-Blocking Client

2.阻断与非阻断客户

It’s a common requirement in web applications to make HTTP calls to other services. So, we need a web client tool.

在网络应用中,对其他服务进行HTTP调用是一个常见的要求。所以,我们需要一个网络客户端工具。

2.1. RestTemplate Blocking Client

2.1.RestTemplate 封锁客户端

For a long time, Spring has been offering RestTemplate as a web client abstraction. Under the hood, RestTemplate uses the Java Servlet API, which is based on the thread-per-request model.

长期以来,Spring一直在提供RestTemplate作为Web客户端的抽象。在引擎盖下,RestTemplate使用了Java Servlet API,它是基于每请求线程模型。

This means that the thread will block until the web client receives the response. The problem with the blocking code is due to each thread consuming some amount of memory and CPU cycles.

这意味着该线程将被阻塞,直到网络客户端收到响应。阻塞代码的问题是由于每个线程都会消耗一定量的内存和CPU周期。

Let’s consider having a lot of incoming requests, which are waiting for some slow service needed to produce the result.

让我们考虑有很多传入的请求,这些请求正在等待一些需要产生结果的缓慢服务。

Sooner or later, the requests waiting for the results will pile up. Consequently, the application will create many threads, which will exhaust the thread pool or occupy all the available memory. We can also experience performance degradation because of the frequent CPU context (thread) switching.

迟早,等待结果的请求会堆积如山。因此,应用程序将创建许多线程,这将耗尽线程池或占用所有可用的内存。由于频繁的CPU上下文(线程)切换,我们还可能遇到性能下降的情况。

2.2. WebClient Non-Blocking Client

2.2.WebClient非阻塞式客户端

On the other side, WebClient uses an asynchronous, non-blocking solution provided by the Spring Reactive framework.

在另一边,WebClient使用Spring Reactive框架提供的异步、非阻塞解决方案。

While RestTemplate uses the caller thread for each event (HTTP call), WebClient will create something like a “task” for each event. Behind the scenes, the Reactive framework will queue those “tasks” and execute them only when the appropriate response is available.

RestTemplate为每个事件(HTTP调用)使用调用者线程时,WebClient将为每个事件创建类似 “任务 “的东西。在幕后,Reactive框架将对这些 “任务 “进行排队,并在适当的响应可用时才执行。

The Reactive framework uses an event-driven architecture. It provides means to compose asynchronous logic through the Reactive Streams API. As a result, the reactive approach can process more logic while using fewer threads and system resources, compared to the synchronous/blocking method.

反应式框架采用了事件驱动的架构。它提供了通过Reactive Streams API组成异步逻辑的手段。因此,与同步/阻塞方法相比,反应式方法可以处理更多的逻辑,同时使用更少的线程和系统资源。

WebClient is part of the Spring WebFlux library. So, we can also write client code using a functional, fluent API with reactive types (Mono and Flux) as a declarative composition.

WebClientSpring WebFlux库的一部分。因此,我们也可以使用带有反应式类型(MonoFlux)的函数式、流畅的API来编写客户端代码,作为声明式的组合。

3. Comparison Example

3.比较实例

To demonstrate the differences between these two approaches, we’d need to run performance tests with many concurrent client requests.

为了证明这两种方法之间的差异,我们需要用许多并发的客户端请求来进行性能测试。

We would see a significant performance degradation with the blocking method after a certain number of parallel client requests.

在一定数量的并行客户端请求之后,我们会看到阻塞方法的性能显著下降。

However, the reactive/non-blocking method should give constant performances, regardless of the number of requests.

然而,反应式/非阻塞式方法应该提供恒定的性能,无论请求的数量如何。

For this article, we’ll implement two REST endpoints, one using RestTemplate and the other using WebClient. Their task is to call another slow REST web service, which returns a list of tweets.

在本文中,我们将实现两个REST端点,一个使用RestTemplate,另一个使用WebClient它们的任务是调用另一个缓慢的REST Web服务,该服务返回一个推文列表。

To start, we’ll need the Spring Boot WebFlux starter dependency:

首先,我们需要Spring Boot WebFlux启动器依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

And here’s our slow service REST endpoint:

这里是我们的慢速服务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. Using RestTemplate to Call a Slow Service

3.1.使用RestTemplate来调用一个慢速服务

Let’s now implement another REST endpoint that will call our slow service via the web client.

现在让我们实现另一个REST端点,它将通过网络客户端调用我们的慢速服务。

First, we’ll use RestTemplate:

首先,我们将使用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;
}

When we call this endpoint, due to the synchronous nature of RestTemplate, the code will block waiting for the response from our slow service. The rest of the code in this method will be run only when the response has been received.

当我们调用这个端点时,由于RestTemplate的同步性,代码将阻塞,等待来自我们慢速服务的响应。这个方法中的其他代码只有在收到响应后才会被运行。

Here’s what we’ll see in the logs:

下面是我们将在日志中看到的内容。

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. Using WebClient to Call a Slow Service

3.2.使用WebClient来调用一个慢速服务

Second, let’s use WebClient to call the slow service:

第二,让我们使用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;
}

In this case, WebClient returns a Flux publisher, and the method execution gets completed. Once the result is available, the publisher will start emitting tweets to its subscribers.

在这种情况下,WebClient返回一个Flux发布器,方法的执行得到了完成。一旦有了结果,发布者将开始向其订阅者发布推文。

Note that a client (in this case, a web browser) calling this /tweets-non-blocking endpoint will also be subscribed to the returned Flux object.

请注意,调用这个/tweets-non-blocking端点的客户端(在这种情况下,是一个Web浏览器)也将被订阅到返回的Flux对象。

Let’s observe the log this time:

我们这次来观察一下日志。

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)

Note that this endpoint method completed before the response was received.

注意,这个端点方法在收到响应之前就完成了。

4. Conclusion

4.总结

In this article, we explored two different ways of using web clients in Spring.

在这篇文章中,我们探讨了在Spring中使用Web客户端的两种不同方式。

RestTemplate uses Java Servlet API and is therefore synchronous and blocking.

RestTemplate使用Java Servlet API,因此是同步和阻塞的。

Conversely, WebClient is asynchronous and will not block the executing thread while waiting for the response to come back. The notification will be produced only when the response is ready.

相反,WebClient是异步的,在等待响应回来时不会阻塞执行的线程。只有在响应准备好时才会产生通知。

RestTemplate will still be used. But in some cases, the non-blocking approach uses much fewer system resources compared to the blocking one. So, WebClient is a preferable choice in those cases.

RestTemplate仍将被使用。但在某些情况下,非阻塞式方法与阻塞式方法相比,使用的系统资源要少很多。因此,在这些情况下,WebClient是一个更好的选择。

All of the code snippets mentioned in the article can be found over on GitHub.

文章中提到的所有代码片段都可以在GitHub上找到