Aplicaciones Reactivas usando Spring Webflux sobre Oracle Cloud - Parte I

Por Alberto Salazar
Publicado en Octubre 2018

Revisado por Juan Pablo Guizado




La programación reactiva es un nuevo paradigma en el desarrollo de aplicaciones en Java, en esta serie de artículos técnicos vamos a aprender como usar Spring Webflux y sus fundamentos para crear aplicaciones reactivas hasta desplegarlas en Oracle Cloud; comencemos entendiendo el fundamento de una aplicación reactiva.



Aplicación Reactiva:


El principal beneficio de la programación reactiva es el manejar de manera eficiente y efectiva los recursos sobre el que corre la aplicación (Menor uso de hilos en el servidor). Para esto podemos primero explicar la diferencia entre una aplicación web java tradicional que bloquea los hilos del servidor por cada petición versus la aplicación reactiva:


En la aplicación tradicional por cada petición a nuestra aplicación un hilo (thread) es bloqueado hasta procesar su respuesta; con el modelo de aplicación reactiva un hilo podría manejar varias peticiones de manera asíncrona y notificara mediante eventos su respuesta; además el término "reactivo" se refiere construir aplicaciones en base a eventos u en otras palabras a la reacción al cambio; es decir reaccionar a eventos de E/S. En ese sentido, el desbloqueo es reactivo porque en lugar de estar bloqueado hasta terminar el proceso u lógica de negocio con el que inicia la petición (request), ahora estamos atentos a reaccionar a los eventos a medida que se completan las operaciones, lógica de negocio o que disponemos de datos para dar una respuesta.



Spring Webflux:


Desde la versión 5.x de Spring se introduce Spring Webflux como marco de trabajo (framework) de aplicaciones web reactivas. Esta basado en reactive streams (http://www.reactive-streams.org/) y corre en servidores web como: Netty, Undertow, u contenedores que soporte la especificacion de Servlets 3.1 o superior.

Spring Webflux usa el proyecto Reactor como base para la implementación de reactive streams, para ello entendamos que es el proyecto Reactor y sus principales componentes:

Reactor es una implementación de la especificación Reactive Streams que incluye adaptadores que facilitan su uso al interactuar con el API de una manera más simple; vamos a enfocarnos en dos clases principales de productores que manejan los resultados donde podemos aplicar los operadores:

Mono: Mono representa el resultado asíncrono de un valor simple o vacío (0...1) elemento.
Flux: El flujo representa una secuencia asíncrona de 0 a n elementos.



Luego de entender que es una aplicación web reactiva, y los principales elementos a usar del proyecto reactor a continuación crearemos un ejemplo desde 0 para el uso de Spring Webflux.

Para la implementación de spring webflux con spring podemos optar por dos modelos de desarrollo:

  • Anotación en controladores, similar a como se realizaban aplicación usando Spring MVC en el modelo tradicional pero que internamente realizan el soporte reactivo (Reactor, RxJava)
  • Endpoints funcionales (Funcional EndPoints) - Es un modelo basada en lambdas que nos permite usar rutas y manejadores para las peticiones a nuestra aplicación.

Para más detalle y diferencias entre los dos modelos pueden seguir el siguiente link (https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-framework-choice ); en nuestro caso usaremos Endpoint funcionales.



Primero crearemos una aplicación base de springboot usando su inicializador en el siguiente link: https://start.spring.io



Inicializar una aplicación basada en spring reactive web stack




Como referencia hemos ingresado el paquete base de nuestro proyecto “com.otn.reactive”, nombre de nuestro artefacto “reactive-spring-part-I” y en la sección de dependencias vamos a ingresar “reactive web” que nos incluirá webflux y damos click en el botón Generate Project.

Luego podemos abrir el proyecto maven en el IDE de nuestra preferencia (Intellij, Eclipse u otro); vamos a revisar nuestro archivo pom.xml el cual tendrá la dependencia a Webflux y reactor como lo vemos a continuación:



Dependencias en archivo pom.xml de spring reactive web stack




Ahora vamos a crear un punto de interacción (Endpoint) como servicio rest basada en programación funcional usando lambdas que nos permita:

  • GET: Obtener una lista de clientes
  • POST: Guardar un cliente
  • PUT: Actualizar un cliente
  • DELETE: Borrar un cliente 



Api reactivo para manejar clientes




Primero vamos a crear la clase que es nuestro modelo de datos para el servicio rest que represente a un cliente:


package com.otn.reactive.reactivespringpartI;

public class Cliente {

   private String id;

   private String nombres;

   private String apellidos;

   private Integer telefono;

   private String direccion;

   public Cliente(String id, String nombres, String apellidos, Integer 
telefono, String direccion) {
       this.id = id;
       this.nombres = nombres;
       this.apellidos = apellidos;
       this.telefono = telefono;
       this.direccion = direccion;
   }

   public String getId() {
       return id;
   }

   public void setId(String id) {
       this.id = id;
   }

   public String getNombres() {
       return nombres;
   }

   public void setNombres(String nombres) {
       this.nombres = nombres;
   }

   public String getApellidos() {
       return apellidos;
   }

   public void setApellidos(String apellidos) {
       this.apellidos = apellidos;
   }

   public Integer getTelefono() {
       return telefono;
   }

   public void setTelefono(Integer telefono) {
       this.telefono = telefono;
   }

   public String getDireccion() {
       return direccion;
   }

   public void setDireccion(String direccion) {
       this.direccion = direccion;
   }

   @Override
   public String toString() {
       return "Cliente{" +
               "id='" + id + '\'' +
               ", nombres='" + nombres + '\'' +
               ", apellidos='" + apellidos + '\'' +
               ", telefono=" + telefono +
               ", direccion='" + direccion + '\'' +
               '}';
   }
}




Ahora vamos a crear un manejador (handler) de que será el encargado de devolver o ejecutar las petición que nuestra ruta de clientes:


package com.otn.reactive.reactivespringpartI;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

@Component
public class ClienteHandler {

   public Mono<ServerResponse> getAll(ServerRequest request) {
       // Simulamos un repositorio en memoria creando una lista de clientes.
       List<Cliente> clientesRepositorio = new ArrayList<Cliente>();
       clientesRepositorio.add(new Cliente("1001", "Alberto", "Salazar", 
3299999, "Quito, EC170102"));
       clientesRepositorio.add(new Cliente("1002", "Pablo", "Arizaga", 
22332233, "Quito, EC170104"));
       clientesRepositorio.add(new Cliente("1003", "Maria", "Valdez", 
82923233, "Quito, EC178976"));
       clientesRepositorio.add(new Cliente("1004", "Paola", "Llanos", 
87878799, "Quito, EC179087"));
       clientesRepositorio.add(new Cliente("1005", "Tamara", "Cisneros", 
4356772, "Quito, EC876590"));

       Flux<Cliente> clientes = Flux.fromIterable(clientesRepositorio);
       return 
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(clientes, Cliente.class);
   }
}




Finalmente, vamos a definir una ruta (router) para recibir las peticiones de los clientes interesados en resolver las URI de nuestro API Rest:


package com.otn.reactive.reactivespringpartI;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
@EnableWebFlux
@CrossOrigin
public class ClienteRouter {

   @Bean
   public RouterFunction monoRouterFunction(ClienteHandler clienteHandler){        
return route(GET("/reactive/v1/cliente").and(accept(MediaType.APPLICATION_JSON)),  
clienteHandler::getAll);    } } 




Finalmente podemos ejecutar la aplicación en la raiz del proyecto en consola ejecutando:

mvn spring-boot:run




Al ejecutar la aplicación vamos a ver en consola:



Y podemos ejecutar en nuestro browser la siguiente url: http://localhost:8080/reactive/v1/cliente y obtendremos nuestra lista de clientes; también podemos realizar vía consola una petición con curl:

curl -X GET http://localhost:8080/reactive/v1/cliente 




El resultado sería:


[{"id":"1001","nombres":"Alberto","apellidos":"Salazar","telefono":3299999,"direccion":
"Quito, EC170102"},{"id":"1002","nombres":"Pablo","apellidos":"Arizaga","telefono":22332233,
"direccion":"Quito, EC170104"},{"id":"1003","nombres":"Maria","apellidos":"Valdez",
"telefono":82923233,"direccion":"Quito, EC178976"},{"id":"1004","nombres":"Paola",
"apellidos":"Llanos","telefono":87878799,"direccion":"Quito, EC179087"},{"id":"1005",
"nombres":"Tamara","apellidos":"Cisneros","telefono":4356772,"direccion":"Quito, EC876590"}]
    




El proyecto completo que incluye los metodos de guardar, actualizar y borrar clientes lo pueden encontrar en github en el siguiente link: https://github.com/lasalazarr/reactive-spring-part-I



Resumen


A través de este artículo hemos entendido cómo crear una aplicación reactiva; incluyendo un modelo de datos que tenga un flujo de ejecución mediante servicios rest de manera asíncrona que es el modelo similar al crear soluciones usando Node.js; en la siguiente parte de este artículo complementaremos nuestro ejemplo aprendiendo más sobre el API cliente de WebFlux y complementaremos el artículo con el despliegue de la aplicación en Oracle Cloud.




Alberto Salazar es un profesional con más de 17 años de experiencia en la creación de arquitecturas Java para sistemas escalables y de gran carga de transacciones, desde J2EE 1.3, honrado como Auth0 Ambassador y coautor del libro 'Software Architecture with Spring 5.0'.
JUG Leader, Auth0Ambassador & JCP Member. Fundador de Java Users Group Ecuador y Javaday Ecuador Conference. Ponente en conferencias de clase mundial tales como: OracleCodeOne, JavaOne, Oracle Code, Redhat Summit, Voxxed Days, Oracle Code Latam Tour, Oracle OTN LAD y el Grupo de Usuarios de Java Ecuador.

Este artículo ha sido revisado por el equipo de productos Oracle y se encuentra en cumplimiento de las normas y prácticas para el uso de los productos Oracle.