1. Overview
In this tutorial, we’re going to compare two of Spring’s web client implementations — RestTemplate and new Spring 5’s reactive alternative WebClient.
2. Blocking vs Non-Blocking Client
It’s a common requirement in web applications to make HTTP calls to other services. So, we need a web client tool.
2.1. RestTemplate Blocking Client
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.
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.
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.
2.2. WebClient Non-Blocking Client
On the other side, WebClient uses an asynchronous, non-blocking solution provided by the Spring Reactive framework.
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.
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.
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.
3. Comparison Example
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.
To start, we’ll need the Spring Boot WebFlux starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
And here’s our slow service REST endpoint:
@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
Let’s now implement another REST endpoint that will call our slow service via the web client.
First, we’ll use 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.
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
Second, let’s use WebClient to call the slow service:
@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.
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.
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
In this article, we explored two different ways of using web clients in Spring.
RestTemplate uses Java Servlet API and is therefore synchronous and blocking.
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.
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.
All of the code snippets mentioned in the article can be found over on GitHub.