Pour les plus curieux ou informés d’entre vous il se peut que vous soyez tombés sur ce message dans la doc de notre cher et tendre RestTemplate.

NOTE: As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward.

Et là c’est le drame ! Adieu veaux, vaches, cochons, couvée, notre client REST favori a du plomb dans l’aile au profit d’un petit nouveau (et nous allons voir que pas tant que ça) nommé WebClient se trouvant dans un mystérieux package “reactive”.

Historique

A l’origine la grande majorité des applications web Java sont construites sur l’API Servlet conçue pour les échanges client/serveur synchrones et bloquants. Les années passent et, devant le besoin grandissant, le support des requêtes asynchrones (Servlet 3.0 en 2009) puis ensuite des requêtes non bloquantes (plus de détails dans (1)) (Servlet 3.1 en 2013) ont été introduit. 

Malgré ces ajouts, à ce jour, la majorité des applications web Java tournent sur des moteurs ou conteneurs de Servlet (que l’on désigne souvent directement par “serveur”) comme Apache Tomcat ou Jetty. Dans ces configurations, la stack web Spring MVC est utilisée et se base sur un traitement synchrone des requêtes.

Crédits : https://howtodoinjava.com/spring-webflux/spring-webflux-tutorial/

 

 

A l’opposé, un système de traitement de requêtes asynchrones non-bloquant comme NodeJs peut le proposer, fait qu’aucun Thread n’est en attente.

Crédits : https://howtodoinjava.com/spring-webflux/spring-webflux-tutorial/

J’te bloque

Reactive Streams API

L’API Reactive Streams (https://www.reactive-streams.org/) est une proposition de standard décrivant le traitement des flux asynchrones via un système au doux nom de « non-blocking back pressure« . Il repose sur des notions de Publisher / Subscriber / Processor. Ce standard a été créé par des ingénieurs de diverses entités comme Netflix, Pivotal, Lightbend, RedHat, Twitter, et Oracle entre autres et se trouve désormais inclus au sein de Java depuis la version 9.

Project Reactor

Tout bon standard vient avec sa flopée d’implémentations. Ainsi les deux implémentations les plus populaires sont :

C’est ce dernier qui va nous intéresser…

Spring WebFlux

Spring WebFlux est le pendant de Spring MVC pour la programmation réactive. Il utilise la librairie project-reactor et le serveur embarqué Netty (https://netty.io/) pour proposer une librairie web complète non-bloquante, et ce, depuis la version 5 de Spring.


Au delà de sa capacité réactive, ce framework s’articule autour d’une API moderne (coucou RestTemplate) :

  • Basée sur des annotations
  • fluent (chaînée) des composants de la librairie
  • Fonctionnelle (Lambdas)
  • Et avec d’autres principes et joyeusetés comme l’immutabilité, les builders ou encore les factory pattern

Flux et Mono

Spring WebFlux via reactor s’utilise au travers de deux publishers tous les deux typés par TT est le type de l’élément retourné :

  • Mono<T> : retourne 0 ou 1 élément.
Mono<Integer> mono = Mono.just(42);
Mono<Integer> mono = Mono.empty();
  • Flux<T> : retourne 0…n éléments
Flux.just(
        Person.of("Jane", "Doe"),
        Person.of("John", "Doe")
    );
Flux.fromArray(new Person[]{Person.of("Jane", "Doe"), Person.of("Jane", "Doe")});
Flux.fromIterable(List.of(Person.of("Jane", "Doe"), Person.of("Jane", "Doe")));

Et dans Spring WebFlux, à partir du moment où nous appelons une API ou fonction réactive, celle-ci retournera un publisher de type Mono ou Flux.

Et mon RestTemplate dans tout ça

Dans les faits, ces bouts de code devraient vous rappeler quelque chose :

  1. La configuration Spring pour injecter le restTemplate
@Bean
public RestTemplate restTemplate() {
   return new RestTemplateBuilder()
           .defaultMessageConverters()
           .interceptors(loggingInterceptor())
           .build();
}

2. Un exemple d’appel externe via restTemplate

public Result getResult() {
   final var uriComponents = UriComponentsBuilder.newInstance()
           .scheme(apiConfig.getScheme())
           .host(apiConfig.getHost())
           .port(apiConfig.getPort())
           .path(apiConfig.getPath())
           .build();

   final var response = restTemplate.getForEntity(uriComponents.toUri(), Result.class);

   if (response.getStatusCode().is2xxSuccessful()) {
       return response.getBody();
   }
   throw new ApiException(response.getStatusCode());

Ici le restTemplate est utilisé pour récupérer une réponse de type HttpEntity<Result> sur un appel HTTP GET synchrone.

WebClient

Comme indiqué dans la note présente dans la documentation de RestTemplate, son fringant remplaçant se prénomme WebClient et se trouve au sein de Spring WebFlux. Il faut donc ajouter (ou remplacer Spring MVC pour les plus aventureux) la dépendance suivante :

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

La configuration du WebClient s’effectue de son côté comme suit :

@Bean
public WebClient webClient() {
   return WebClient.builder()
       .baseUrl(apiConfig.getBaseUrl())
       .filter(loggingFilter())
       .build();
}

Notons que la notion d’interceptor et remplacée par la notion de filter.

Et, roulements de tambour, voici l’équivalent d’un HTTP GET REST synchrone.

public Result getResult() {
   final var response = webClient.get()
           .uri(apiConfig.getPath())
           .retrieve()
           .onStatus(HttpStatus::isError, Mono.error(new MyCustomClientException()) )
           .toEntity(Result.class)
           .block();
   return response.getBody();
}

Dans cet exemple, on remarque bien le caractère beaucoup plus fonctionnel et fluent du WebClient vis-à-vis de son alter-ego synchrone RestTemplate. On manipule dans notre cas un publisher de type Mono<Result>

Il est évidemment possible de passer un body ou des headers via les méthodes éponymes.

  final var response = webClient.post()
           .uri(apiConfig.getPath())
           .body(BodyInserters.fromValue(bodyObject))
           .header(HttpHeaders.AUTHORIZATION, "Bearer XXX")

L’autre point à retenir est l’appel à la méthode block() qui permet d’exécuter notre requête HTTP de manière synchrone. D’ailleurs si on regarde de plus près ce que fait cette méthode, on retrouve bien la notion de subscriber présentée dans le standard Reactive Streams.

public T block() {
  BlockingMonoSubscriber<T> subscriber = new BlockingMonoSubscriber<>();
  subscribe((Subscriber<T>) subscriber);
  return subscriber.blockingGet();
}

“Ready to roll down”

Voilà, désormais, vous êtes parés au changement ! Vous avez l’équivalent WebClient synchrone et bloquant de votre bon vieux RestTemplate.

Evidemment cela soulève beaucoup de questions :

  • Quand est-ce que Spring sonnera le glas de l’ancien monde via son @Deprecated jupitérien ? Est-ce que cela sonnera la fin de Spring MVC au profil de WebFlux ? Dans ce dernier cas, ce n’est pas la même histoire… une stack réactive complète (de la source de données aux couches API) c’est autre chose que de remplacer seulement les appels synchrones du RestTemplate. Il est d’ailleurs à noter que l’homologue asynchrone du RestTemplate (le bien nommé AsyncRestTemplateest déjà déprécié au profit du WebClient…
  • Il est désormais ouvertement assumé que Spring <3 Kotlin (il n’est pas seul :)) et le support des coroutines (une autre façon de gérer les appels asynchrones non bloquants) dans WebFlux n’est-ce pas également un fort indicateur de la volonté de Pivotal de pousser son framework ?

L’avenir nous le dira. En tout cas, chez Liksi, on suit ça de près.