Spring WebClient frente a RestTemplate

DESCANSO Arriba

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO

1. Introducción

En este tutorial, vamos a comparar dos de las implementaciones del cliente web de Spring: RestTemplate y el nuevo WebClient alternativo reactivo de Spring 5 .

2. Cliente bloqueante frente a cliente no bloqueante

Es un requisito común en las aplicaciones web realizar llamadas HTTP a otros servicios. Por lo tanto, necesitamos una herramienta de cliente web.

2.1. Cliente de bloqueo RestTemplate

Durante mucho tiempo, Spring ha estado ofreciendo RestTemplate como una abstracción de cliente web. Bajo el capó, RestTemplate utiliza la API de Java Servlet, que se basa en el modelo de subprocesos por solicitud .

Esto significa que el hilo se bloqueará hasta que el cliente web reciba la respuesta. El problema con el código de bloqueo se debe a que cada hilo consume cierta cantidad de memoria y ciclos de CPU.

Consideremos tener muchas solicitudes entrantes, que están esperando algún servicio lento necesario para producir el resultado.

Tarde o temprano, las solicitudes que esperan los resultados se acumularán. En consecuencia, la aplicación creará muchos subprocesos, que agotarán el grupo de subprocesos u ocuparán toda la memoria disponible . También podemos experimentar una degradación del rendimiento debido al cambio frecuente de contexto (hilo) de la CPU.

2.2. Cliente sin bloqueo de WebClient

Por otro lado, WebClient utiliza una solución asincrónica sin bloqueo proporcionada por el marco Spring Reactive .

Mientras RestTemplate usa el hilo de la persona que llama para cada evento (llamada HTTP), WebClient creará algo así como una "tarea" para cada evento. Detrás de escena, el marco reactivo pondrá en cola esas "tareas" y las ejecutará solo cuando la respuesta adecuada esté disponible.

El marco reactivo utiliza una arquitectura basada en eventos. Proporciona medios para componer lógica asincrónica a través de la API Reactive Streams. Como resultado, el enfoque reactivo puede procesar más lógica mientras usa menos subprocesos y recursos del sistema, en comparación con el método síncrono / de bloqueo.

WebClient es parte de la biblioteca Spring WebFlux. Por lo tanto, también podemos escribir código de cliente utilizando una API fluida y funcional con tipos reactivos ( Mono y Flux ) como composición declarativa .

3. Ejemplo de comparación

Para demostrar las diferencias entre estos dos enfoques, necesitaríamos ejecutar pruebas de rendimiento con muchas solicitudes de clientes simultáneas. Veríamos una degradación significativa del rendimiento con el método de bloqueo después de una cierta cantidad de solicitudes de clientes paralelas.

Por otro lado, el método reactivo / no bloqueante debería ofrecer un rendimiento constante, independientemente del número de solicitudes.

Para el propósito de este artículo, implementemos dos puntos finales REST, uno usando RestTemplate y el otro usando WebClient . Su tarea es llamar a otro servicio web REST lento, que devuelve una lista de tweets.

Para empezar, necesitaremos la dependencia de inicio Spring Boot WebFlux:

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

Además, aquí está nuestro punto final REST de servicio lento:

@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. Usar RestTemplate para llamar a un servicio lento

Implementemos ahora otro punto final REST que llamará a nuestro servicio lento a través del cliente web.

En primer lugar, usaremos 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; }
     
    

Cuando llamamos a este punto final, debido a la naturaleza sincrónica de RestTemplate , el código se bloqueará esperando la respuesta de nuestro servicio lento. Solo cuando se haya recibido la respuesta, se ejecutará el resto del código de este método. En los registros, veremos:

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. Uso de WebClient para llamar a un servicio lento

En segundo lugar, usemos WebClient para llamar al servicio lento:

@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; }

En este caso, WebClient devuelve un editor Flux y se completa la ejecución del método. Una vez que el resultado esté disponible, el editor comenzará a emitir tweets a sus suscriptores. Tenga en cuenta que un cliente (en este caso, un navegador web) que llame a este punto final / tweets-non- block también se suscribirá al objeto Flux devuelto .

Observemos el registro esta vez:

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])

Tenga en cuenta que este método de punto final se completó antes de que se recibiera la respuesta.

4. Conclusión

En este artículo, exploramos dos formas diferentes de usar clientes web en Spring.

RestTemplate usa Java Servlet API y, por lo tanto, es síncrono y bloqueante. Por el contrario, WebClient es asincrónico y no bloqueará el subproceso en ejecución mientras espera a que vuelva la respuesta. Solo cuando la respuesta esté lista se producirá la notificación.

RestTemplate se seguirá utilizando. En algunos casos, el enfoque sin bloqueo utiliza muchos menos recursos del sistema en comparación con el de bloqueo. Por lo tanto, en esos casos, WebClient es una opción preferible.

Todos los fragmentos de código, mencionados en el artículo, se pueden encontrar en GitHub.

DESCANSO inferior

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO