1. Introduction
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. Therefore, 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 creates a new 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. Therefore, we can additionally 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’d see a significant performance degradation with the blocking method after a certain number of parallel client requests.
On the other side, the reactive/non-blocking method should give constant performances, regardless of the number of requests.
For the purpose of this article, let’s 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.
For a start, we’ll need the Spring Boot WebFlux starter dependency:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
Furthermore, 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 which will call our slow service via the web client.
Firstly, 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. Only when the response has been received, the rest of the code in this method will be executed. In the logs, we’ll see:
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
Secondly, 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. Contrarily, WebClient is asynchronous and will not block the executing thread while waiting for the response to come back. Only when the response is ready will the notification be produced.
RestTemplate will still be used. In some cases, the non-blocking approach uses much fewer system resources compared to the blocking one. Hence, in those cases, WebClient is a preferable choice.
All of the code snippets, mentioned in the article, can be found over on GitHub.