Saltar al contenido

Springboot: Cómo usar WebClient en lugar de RestTemplate para realizar llamadas asíncronas y sin bloqueo

Te doy la bienvenida a nuestra página, aquí vas a hallar la resolución que buscabas.

Solución:

Debido a que hay muchos conceptos erróneos, aquí voy a aclarar algunas cosas.

Spring ha declarado oficialmente que desaprobarán RestTemplate en el futuro, así que si puedes, usa WebClient si quiere estar lo más preparado posible para el futuro.

como se indica en la API RestTemplate

NOTA: A partir de 5.0, el reactivo no bloqueante org.springframework.web.reactive.client.WebClient ofrece una alternativa moderna a la RestTemplate con soporte eficiente para sincronización y async, así como escenarios de transmisión. los RestTemplate quedará obsoleto en una versión futura y no se agregarán nuevas funciones importantes en el futuro. Ver el WebClient sección de la documentación de referencia de Spring Framework para obtener más detalles y código de ejemplo.

Aplicación no reactiva

Si su aplicación es una aplicación no reactiva (que no devuelve flujos o monos a los clientes que llaman), lo que debe hacer es usar block() si necesita el valor. Por supuesto que puedes usar Mono o Flux internamente en su aplicación pero al final debe llamar block() para obtener el valor concreto que necesita para devolver al cliente que llama.

Uso de aplicaciones no reactivas tomcat como la implementación del servidor subyacente, que es un servidor basado en servlet que asignará 1 subproceso por solicitud para que no obtenga las ganancias de rendimiento que obtiene con una aplicación reactiva.

Aplicación reactiva

Si por el contrario tienes una aplicación reactiva, nunca bajo ninguna circunstancia deberías llamar block() en su aplicación. El bloqueo es exactamente lo que dice, bloqueará un hilo y bloqueará la ejecución de ese hilo hasta que pueda seguir adelante, esto es malo en un mundo reactivo.

Tampoco deberías llamar subscribe en su aplicación a menos que su aplicación sea el consumidor final de la respuesta. Por ejemplo, si está llamando a una API para obtener datos y escribir en una base de datos a la que está conectada su aplicación. Su aplicación de backend es el consumidor final. Si un cliente externo está llamando a su backend (por ejemplo, una aplicación de reacción, angular, cliente móvil, etc., etc.), el cliente externo es el consumidor final y es el que se suscribe. No tú.

La implementación del servidor predeterminado subyacente aquí es un netty servidor que no es un servlet, servidor basado en eventos que no asigne un hilo a cada solicitud, el servidor en sí es independiente del hilo y cualquier hilo disponible manejará cualquier cosa en cualquier momento durante cualquier solicitud.

La documentación de webflux establece claramente que tanto los servidores compatibles con servlet 3.1+, tomcat y jetty, se pueden usar con webflux como con servidores que no son serlet, netty y undertow.

¿Cómo sé qué aplicación tengo?

Spring dice que si tienes ambos spring-web y spring-webflux en el classpath, la aplicación favorecerá spring-web y, por defecto, inicie una aplicación no reactiva con un servidor Tomcat subyacente.

Este comportamiento se puede anular manualmente si es necesario como estados de primavera.

Sumando ambos spring-boot-starter-web y spring-boot-starter-webflux Los módulos en su aplicación dan como resultado Spring Boot que configura automáticamente Spring MVC, no WebFlux. Este comportamiento ha sido elegido porque muchos desarrolladores de Spring agregan spring-boot-starter-webflux a su aplicación Spring MVC para usar el WebClient reactivo. Aún puede hacer cumplir su elección configurando el tipo de aplicación elegido en SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE).

El “marco de Spring WebFlux”

Entonces, ¿cómo implementar WebClient

@Retryable(maxAttempts = 4,
       value = java.net.ConnectException.class,
       backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity getResponse(String url) 
    return webClient.get()
            .uri(url)
            .exchange()
            .flatMap(response -> response.toEntity(String.class))
            .block();

Esta es la implementación más fácil y menos intrusiva. Por supuesto, necesita crear un cliente web adecuado en tal vez un @Bean y conectarlo automáticamente a su clase.

Lo primero que debe comprender es si necesita llamar .block() también podrías quedarte con RestTemplate, usar WebClient no le reportará nada.

Debe comenzar a pensar en términos reactivos si desea beneficiarse del uso de WebClient. Un proceso reactivo es en realidad solo una secuencia de pasos, la entrada de cada paso es la salida del paso anterior. Cuando entra una solicitud, su código crea la secuencia de pasos y regresa inmediatamente liberando el hilo http. Luego, el marco usa un grupo de subprocesos de trabajo para ejecutar cada paso cuando la entrada del paso anterior está disponible.

El beneficio es una gran ganancia en la capacidad de aceptar solicitudes de la competencia con el pequeño costo de tener que repensar la forma en que escribe el código. Su aplicación solo necesitará un grupo muy pequeño de subprocesos http y otro grupo muy pequeño de subprocesos de trabajo.

Cuando su método de controlador devuelve un Mono o Flux, lo ha hecho bien y no habrá necesidad de llamar block().

Algo como esto en su forma más simple:

@GetMapping(value = "endpoint", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
@ResponseStatus(OK)
public Mono controllerMethod() 

    final UriComponentsBuilder builder =
            UriComponentsBuilder.fromHttpUrl("http://base.url/" + "endpoint")
                    .queryParam("param1", "value");

    return webClient
            .get()
            .uri(builder.build().encode().toUri())
            .accept(APPLICATION_JSON_UTF8)
            .retrieve()
            .bodyToMono(String.class)
            .retry(4)
            .doOnError(e -> LOG.error("Boom!", e))
            .map(s -> 

                // This is your transformation step. 
                // Map is synchronous so will run in the thread that processed the response. 
                // Alternatively use flatMap (asynchronous) if the step will be long running. 
                // For example, if it needs to make a call out to the database to do the transformation.

                return s.toLowerCase();
            );

Pasar a pensar en reactivo es un cambio de paradigma bastante grande, pero vale la pena el esfuerzo. Aguanta, realmente no es tan difícil una vez que puedes entender que no tienes ningún código de bloqueo en toda tu aplicación. Construya los pasos y devuélvalos. Luego, deje que el marco gestione las ejecuciones de los pasos.

Me complace brindar más orientación si algo de esto no está claro.

Recuerda divertirte 🙂

Reseñas y calificaciones de la guía

Tienes la posibilidad dar visibilidad a este post si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *