Spring WebClient contre RestTemplate

Haut REST

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Introduction

Dans ce didacticiel, nous allons comparer deux des implémentations de client Web de Spring - RestTemplate et la nouvelle alternative réactive WebClient de Spring 5 .

2. Client bloquant ou non bloquant

Il est courant dans les applications Web de passer des appels HTTP vers d'autres services. Par conséquent, nous avons besoin d'un outil client Web.

2.1. Client de blocage RestTemplate

Depuis longtemps, Spring propose RestTemplate comme une abstraction de client Web. Sous le capot, RestTemplate utilise l'API Java Servlet, qui est basée sur le modèle de thread par demande .

Cela signifie que le thread se bloquera jusqu'à ce que le client Web reçoive la réponse. Le problème avec le code de blocage est dû au fait que chaque thread consomme une certaine quantité de mémoire et de cycles CPU.

Considérons avoir beaucoup de demandes entrantes, qui attendent un service lent nécessaire pour produire le résultat.

Tôt ou tard, les demandes en attente de résultats vont s'accumuler. Par conséquent, l'application créera de nombreux threads, ce qui épuisera le pool de threads ou occupera toute la mémoire disponible . Nous pouvons également subir une dégradation des performances en raison du changement fréquent de contexte CPU (thread).

2.2. Client non bloquant WebClient

De l'autre côté, WebClient utilise une solution asynchrone et non bloquante fournie par le framework Spring Reactive .

Alors que RestTemplate utilise le thread de l'appelant pour chaque événement (appel HTTP), WebClient créera quelque chose comme une «tâche» pour chaque événement. En coulisse, le cadre réactif mettra en file d'attente ces «tâches» et les exécutera uniquement lorsque la réponse appropriée est disponible.

Le cadre réactif utilise une architecture événementielle. Il fournit des moyens de composer une logique asynchrone via l'API Reactive Streams. En conséquence, l'approche réactive peut traiter plus de logique tout en utilisant moins de threads et de ressources système, par rapport à la méthode synchrone / bloquante.

WebClient fait partie de la bibliothèque Spring WebFlux. Par conséquent, nous pouvons en plus écrire du code client en utilisant une API fonctionnelle et fluide avec des types réactifs ( Mono et Flux ) comme composition déclarative .

3. Exemple de comparaison

Pour démontrer les différences entre ces deux approches, nous aurions besoin d'exécuter des tests de performances avec de nombreuses demandes client simultanées. Nous verrions une dégradation significative des performances avec la méthode de blocage après un certain nombre de requêtes clients parallèles.

D'un autre côté, la méthode réactive / non bloquante devrait donner des performances constantes, quel que soit le nombre de requêtes.

Pour les besoins de cet article, implémentons deux points de terminaison REST, l'un à l'aide de RestTemplate et l'autre à l'aide de WebClient . Leur tâche est d'appeler un autre service Web REST lent, qui renvoie une liste de tweets.

Pour commencer, nous aurons besoin de la dépendance de démarrage Spring Boot WebFlux:

 org.springframework.boot spring-boot-starter-webflux 

En outre, voici notre point de terminaison REST de service lent:

@GetMapping("/slow-service-tweets") private List 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. Utilisation de RestTemplate pour appeler un service lent

Implémentons maintenant un autre point de terminaison REST qui appellera notre service lent via le client Web.

Tout d'abord, nous utiliserons RestTemplate :

@GetMapping("/tweets-blocking") public List getTweetsBlocking() { log.info("Starting BLOCKING Controller!"); final String uri = getSlowServiceUri(); RestTemplate restTemplate = new RestTemplate(); ResponseEntity
    
      response = restTemplate.exchange( uri, HttpMethod.GET, null, new ParameterizedTypeReference
     
      (){}); List result = response.getBody(); result.forEach(tweet -> log.info(tweet.toString())); log.info("Exiting BLOCKING Controller!"); return result; }
     
    

Lorsque nous appelons ce point de terminaison, en raison de la nature synchrone de RestTemplate , le code bloquera l'attente de la réponse de notre service lent. Ce n'est que lorsque la réponse a été reçue que le reste du code de cette méthode sera exécuté. Dans les journaux, nous verrons:

Starting BLOCKING Controller! Tweet(text=RestTemplate rules, [email protected]) Tweet(text=WebClient is better, [email protected]) Tweet(text=OK, both are useful, [email protected]) Exiting BLOCKING Controller!

3.2. Utilisation de WebClient pour appeler un service lent

Deuxièmement, utilisons WebClient pour appeler le service lent:

@GetMapping(value = "/tweets-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux getTweetsNonBlocking() { log.info("Starting NON-BLOCKING Controller!"); Flux 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; }

Dans ce cas, WebClient renvoie un éditeur Flux et l'exécution de la méthode se termine. Une fois le résultat disponible, l'éditeur commencera à émettre des tweets à ses abonnés. Notez qu'un client (dans ce cas, un navigateur Web) appelant ce point de terminaison / tweets-non-blocking sera également abonné à l' objet Flux renvoyé .

Observons le journal cette fois:

Starting NON-BLOCKING Controller! Exiting NON-BLOCKING Controller! Tweet(text=RestTemplate rules, [email protected]) Tweet(text=WebClient is better, [email protected]) Tweet(text=OK, both are useful, [email protected])

Notez que cette méthode de point de terminaison s'est terminée avant la réception de la réponse.

4. Conclusion

Dans cet article, nous avons exploré deux façons différentes d'utiliser les clients Web dans Spring.

RestTemplate utilise l'API Java Servlet et est donc synchrone et bloquant. Au contraire, WebClient est asynchrone et ne bloquera pas le thread en cours d'exécution en attendant que la réponse revienne. Ce n'est que lorsque la réponse est prête que la notification sera produite.

RestTemplate sera toujours utilisé. Dans certains cas, l'approche non bloquante utilise beaucoup moins de ressources système que l'approche bloquante. Par conséquent, dans ces cas, WebClient est un choix préférable.

Tous les extraits de code mentionnés dans l'article se trouvent à l'adresse over sur GitHub.

REST bas

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS