Antes de empezar, responderemos qué es la programación reactiva y cuál es su propósito. La programación reactiva es un paradigma de programación y su objetivo principal es crear componentes que sean asíncronos y no bloqueantes. Una parte fundamental de este paradigma son los stream de datos los cuales representan un flujo constante de datos de los cuales se puede generar una respuesta inmediata, dependiendo de lo que se requiera hacer con este flujo de datos.
En esta publicación nos centraremos en la programación reactiva con Java SpringBoot y Spring WebFlux que es parte de spring 5 y se basa en el frameworkProject reactor para así dar soporte a la programación reactiva.
Diferencia entre programación reactiva y programación no reactiva
La principal diferencia radica en que la programación reactiva es no bloqueante y asíncrona, mientras que la programaciónno reactiva es bloqueante y síncrona. Esto quiere decir que, por ejemplo, cuando hacemos una simple Rest API como se hace normalmente, acá cuando se realiza una request el servidor crea un hilo que se encarga de realizar el trabajo, y mientras esto ocurre el hilo encargado de hacer esto queda bloqueado esperando a que la solicitud se resuelva para poder continuar, en el intertanto se pierde mucho tiempo esperando el resultado de dicha solicitud. Es aquí cuando entra la programación reactiva a resolver este problema.
• Generalmente en programación no reactiva tenemos los siguiente:
Como se puede observar en la imagen arriba en el modelo clásico, el servidor crea un hilo por cada request que es enviada, esto ocasiona una serie de problemas ya que normalmente cada aplicación cuenta con un Nro. limitado de hilos que puede crear, y si esto se requiere aumentar implica un claro aumento de recursos lo cual lo hace poco escalable.
• Ahora veamos el modelo para programación reactiva no bloqueante.
A diferencia del modelo anterior, donde se creaba un hilo por request y este quedaba bloqueado hasta tener respuesta, en la programación reactiva se cuenta con un “event loop” el cual tiene un hilo generalmente (puede haber más de uno, pero siempre una cantidad pequeña) el cual se encarga de ir procesando las solicitudes una a una, las cuales quedan con “registro callback”, que queda en una cola cuando este está listo para ser procesado. De esta forma el hilo del “event loop” sabe cuándo procesar las cada response.
Veamos el mismo modelo anterior ahora con programación reactiva:
Como se puede observar, en este modelo no hay bloqueo de hilos como en el caso anterior, esto se debe principalmente que se maneja a través de eventos, entonces al generar una request esta es tomada como un evento, que toma el hilo del event loop y registra este evento con un callback para saber cuando el evento se completa. Esto logra que un único hilo pueda manejar muchas solicitudes a la vez, ya que no es necesario que el hilo espere a que la operación en la BD termine para poder tomar otra request.
Algo importante de destacar es que no todos los servidores y BD pueden usar “reactive programing”, para lograr esto se tiene que cumplir con una serie de condiciones. Como esto se enfoca en java y SringBoot solo se hará mención a las condiciones necesarias para lograr “reactive programing” con Java + SpringBoot:
• Java 8 +
• Servidor de aplicaiones Netty o Tomcat 8.5 + con servlet 3.1 +
• SpringBoot 2.2 +
• BD no-sql o BD relacionales con soporte del driver “R2DBC”
o R2DBC: Reactive Relational Database Connectivity
Ahora continuemos con un poco más de detalle de como los Stream reactivos funcionan:
Los Stream reactivos están compuesto por cuatro partes:
• Publisher: En palabras simples, es quien emite los eventos (data), generalmente es una Base de datos, alguna api externa, archivo de texto, etc. Es quien emite los eventos que el “subscriber” utiliza.
public interface Publisher<T> {
public void subscribe (Subscriber<? super T> s);
}
• Subscriber: Es el que recibe los eventos generados por el Publisher. Una característica importante es que puede haber múltiples Subscribers suscritos a un solo Publisher y procesar los eventos de diferentes maneras.
public interface Subscriber<T> {
public void onSubscribe (Subscription s);
public void onNext (T t);
public void onError(Throwable t);
public void onComplete();
}
• Subscription: Es la relación entre el Publisher y el Subscriber. Cada Subscription es una relación uno a uno, es decir, por cada Subscriber y Publisher hay una Subscription. Puede ser usado tanto para pedir datos como para cancelar las peticiones de data.
public interface Subscription {
public void request(long n);
public void cancel();
}
• Processor: Representa la parte de procesamiento de los datos tanto del Publisher como del Subscriber.
• Los tipos de Stream reactivos son Flux y Mono. Estos son dos implementaciones de un Publisher definidos en “proyect reactor”, el cual es la base para la programación reactiva de la JVM.
o Flux es un es Publisher que emite 0 hasta N elementos.
o Mono es un Publisher igualmente pero solo emite 0 o 1 elementos.
Conclusiones:
Podemos observar cómo los hilos en el modelo de programación reactiva no quedan bloqueados a la espera de la respuesta de la BD. Esto favorece a la hora de optimizar recursos como memoria, que es muy importante tener controlado para no tener errores inesperados por falta de memoria.
Este uso eficiente de recursos también hace que las aplicaciones construidas bajo programación reactiva sean altamente escalables.
Usar programación reactiva trae muchos beneficios, pero no siempre es posible implementarla con base de datos relacionales, ya que se tiene que contar con el soporte para R2DBC. Un ejemplo es la base de datos Oracle, una de las más usadas y que aún tienen versiones iniciales de R2DBC que exigen por ejemplo Java 11, que la gran mayoría de sistemas aun no tienen.
En la siguiente publicación se hará una implementación de un api reactivo con WebFlux.