The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
Edit this Page

Arquitectura Reactiva Quarkus

Quarkus es reactivo. Es incluso más que esto: Quarkus unifica la programación reactiva y la imperativa. Ni siquiera tiene que elegir: puede implementar componentes reactivos y componentes imperativos y luego combinarlos dentro de la misma aplicación. No es necesario usar diferentes stacks, herramientas o APIs; Quarkus conecta ambos mundos.

Esta página explicará lo que entendemos por Reactivo y cómo Quarkus lo habilita. También se abordarán los modelos de ejecución y programación. Por último, se enumerarán las extensiones de Quarkus que ofrecen capacidades reactivas.

¿Qué es Reactivo?

La palabra Reactivo está sobrecargada y asociada a muchos conceptos como la contrapresión, las mónadas o la arquitectura basada en eventos. Por lo tanto, aclaremos qué entendemos por Reactivo.

La Reactividad es un conjunto de principios y directrices para construir sistemas y aplicaciones distribuidos con capacidad de respuesta. El Manifiesto Reactivo caracteriza los Sistemas Reactivos como sistemas distribuidos que tienen cuatro características:

  1. Capacidad de respuesta: deben responder de manera oportuna

  2. Elásticos: se adaptan a las fluctuaciones de la carga

  3. Resilientes: gestionan los fallos de manera controlada

  4. Paso de mensajes asíncrono: los componentes de un sistema reactivo interactúan mediante mensajes

Reactive Systems Pillars

Además de esto, el artículo de Principios Reactivos enumera un conjunto de reglas y patrones para facilitar la construcción de sistemas reactivos.

Sistemas Reactivos y Quarkus

El sistema Reactivo es un estilo arquitectónico que puede resumirse como: sistemas distribuidos bien hechos. El uso de paso de mensajes asíncronos ayuda a reforzar el acoplamiento débil (tanto en términos de espacio como de tiempo) entre los distintos componentes. Se envían mensajes a destinos virtuales. El receptor puede estar situado en cualquier lugar, o incluso no existir aún en el momento del envío del mensaje. El pilar de elasticidad permite escalar hacia arriba o hacia abajo los componentes individuales según la carga. La elasticidad también proporciona redundancia, lo que contribuye al pilar de resiliencia. Los fallos son inevitables. Los componentes que forman un sistema reactivo deben manejarlos de manera elegante, evitar fallos en cascada y adaptarse automáticamente.

Un sistema con capacidad de respuesta puede seguir gestionando la solicitud mientras se enfrenta a fallos y bajo una carga fluctuante. Quarkus ha sido diseñado específicamente para ello. Proporciona características que le ayudarán a diseñar, implementar y operar sistemas reactivos.

Aplicaciones Reactivas

Quarkus no sólo le va a ayudar a construir sistemas reactivos. También garantizará que cada componente cumpla con los principios reactivos y sea altamente eficiente.

La eficiencia es esencial, especialmente en entornos de nube y contenedores. Los recursos, como la CPU y la memoria, se comparten entre varias aplicaciones. Las aplicaciones que consumen grandes cantidades de memoria son ineficientes y penalizan a las aplicaciones coexistentes. Puede ser necesario solicitar más memoria, CPU o máquinas virtuales de mayor capacidad. Esto incrementa el costo mensual en la nube o reduce la densidad de despliegue.

La E/S es una parte esencial de prácticamente cualquier sistema moderno. Ya sea para invocar un servicio remoto, interactuar con una base de datos o enviar mensajes a un intermediario, todas son operaciones basadas en E/S. Gestionarlas de manera eficiente es fundamental para evitar aplicaciones que consuman recursos de forma excesiva. Por esta razón, Quarkus utiliza E/S no bloqueante, lo que permite que un número reducido de hilos del sistema operativo gestione múltiples operaciones de E/S concurrentes. Como resultado, las aplicaciones basadas en Quarkus permiten una mayor concurrencia, consumen menos memoria y mejoran la densidad de despliegue.

¿Cómo Quarkus habilita la Reactividad?

Bajo el capó, Quarkus cuenta con un motor reactivo. Este motor, impulsado por Eclipse Vert.x y Netty, gestiona las interacciones de E/S no bloqueantes.

Quarkus Reactive Core

Las extensiones de Quarkus y el código de la aplicación pueden utilizar este motor para orquestar interacciones de E/S, interactuar con bases de datos, enviar y recibir mensajes, entre otras operaciones.

Modelo de ejecución reactiva

Si bien el uso de E/S no bloqueante ofrece beneficios significativos, no está exento de costos. De hecho, introduce un nuevo modelo de ejecución, bastante diferente del empleado por los frameworks tradicionales.

Las aplicaciones tradicionales utilizan E/S bloqueante y un modelo de ejecución imperativo (secuencial). Así, en una aplicación que expone un endpoint HTTP, cada solicitud HTTP se asocia con un hilo. En general, dicho hilo procesa toda la solicitud y permanece dedicado exclusivamente a esa solicitud durante su ejecución. Cuando el procesamiento requiere interactuar con un servicio remoto, se utiliza E/S bloqueante. El hilo queda bloqueado mientras espera el resultado de la operación de E/S. Aunque este modelo es sencillo de desarrollar (ya que todo ocurre de forma secuencial), presenta varias desventajas. Para manejar solicitudes concurrentes, se requieren múltiples hilos, lo que implica la necesidad de un grupo de hilos de trabajo (worker thread pool). El tamaño de este grupo limita la concurrencia de la aplicación. Además, cada hilo tiene un costo en términos de memoria y CPU. Los grupos de hilos grandes dan lugar a aplicaciones con alto consumo de recursos.

Imperative Execution Model and Worker Threads

Como vimos anteriormente, la E/S no bloqueante evita este problema. Un número reducido de hilos puede manejar múltiples operaciones de E/S concurrentes. Volviendo al ejemplo del endpoint HTTP, el procesamiento de la solicitud se ejecuta en uno de estos hilos de E/S. Dado que son pocos, deben utilizarse de manera eficiente. Cuando el procesamiento de la solicitud requiere invocar un servicio remoto, ya no es posible bloquear el hilo. En su lugar, se programa la operación de E/S y se pasa una continuación, es decir, el código que se ejecutará una vez que la operación de E/S haya finalizado.

Reactive Execution Model and I/O Threads

Este modelo es mucho más eficiente, pero se requiere una forma de escribir código que exprese estas continuaciones.

Modelos de Programación Reactiva

La arquitectura de Quarkus, basada en E/S no bloqueante y paso de mensajes, permite múltiples modelos de desarrollo reactivo compatibles, todos ellos diferenciados en la forma de expresar continuaciones. Las dos principales formas de escribir código reactivo con Quarkus son:

  • Programación Reactiva con Mutiny, y

  • Corutinas con Kotlin

En primer lugar, Mutiny es una biblioteca de programación reactiva intuitiva y dirigida por eventos. Con Mutiny, se escribe código orientado a eventos. Su código es una tubería que recibe eventos y los procesa. Cada etapa de la tubería puede considerarse como una continuación, ya que Mutiny las invoca cuando la parte anterior de la tubería emite un evento.

La API de Mutiny ha sido adaptada para mejorar la legibilidad y el mantenimiento del código. Mutiny proporciona todo lo necesario para orquestar acciones asíncronas, incluida la ejecución concurrente. Además, ofrece un amplio conjunto de operadores para manipular eventos individuales y flujos de eventos.

Encuentre más información sobre Mutiny y su uso en Quarkus en la documentación de soporte de Mutiny.

Las corutinas son una forma de escribir código asíncrono de manera secuencial. Suspenden la ejecución del código durante las operaciones de E/S y registran el resto del código como continuación. Las corutinas de Kotlin son ideales al desarrollar en Kotlin cuando sólo se necesita expresar composiciones secuenciales (cadena de tareas asíncronas interdependientes).

Unificación del enfoque Imperativo y Reactivo

Cambiar su modelo de desarrollo no es sencillo. Requiere reaprender y reestructurar el código para que sea no bloqueante. Afortunadamente, ¡no es necesario hacerlo!

Quarkus es inherentemente reactivo gracias a su motor reactivo. Sin embargo, usted, como desarrollador de aplicaciones, no necesita escribir código reactivo. Quarkus unifica los enfoques reactivo e imperativo. Esto significa que puede desarrollar aplicaciones tradicionales de bloqueo en Quarkus. ¿Pero cómo se evita bloquear los hilos de E/S? Quarkus implementa un patrón proactor que cambia a un hilo de trabajo cuando es necesario.

The proactor pattern in Quarkus

Gracias a las indicaciones en su código (como las anotaciones @Blocking y @NonBlocking), las extensiones de Quarkus pueden determinar cuándo la lógica de la aplicación es bloqueante o no bloqueante. Si volvemos al ejemplo del endpoint HTTP mencionado anteriormente, la solicitud HTTP siempre se recibe en un hilo de E/S. Luego, la extensión que despacha esa solicitud a su código decide si invocarla en el hilo de E/S, evitando cambios de hilo, o en un hilo de trabajo. Esta decisión depende de la extensión. Por ejemplo, la extensión Quarkus REST (anteriormente RESTEasy Reactive) utiliza la anotación @Blocking para determinar si el método debe ejecutarse en un hilo de trabajo o si puede ejecutarse directamente en el hilo de E/S.

Quarkus es pragmático y versátil. Usted decide cómo desarrollar y ejecutar su aplicación. Puede utilizar el enfoque imperativo, el reactivo o una combinación de ambos, aplicando la programación reactiva en las partes de la aplicación que requieren alta concurrencia.

Las extensiones de Quarkus que habilitan la Reactividad

Quarkus ofrece un amplio conjunto de API y características reactivas. Esta sección enumera las más importantes, pero no constituye una lista exhaustiva. Quarkus agrega nuevas funcionalidades en cada versión, y Quarkiverse propone numerosas extensiones que habilitan la Reactividad .

HTTP

  • Quarkus REST: una implementación de Jakarta REST adaptada a la arquitectura de Quarkus. Sigue un enfoque reactivo-primero pero permite código imperativo mediante la anotación @Blocking.

  • Rutas Reactivas: una forma declarativa de registrar rutas HTTP directamente en el enrutador Vert.x utilizado por Quarkus para dirigir las solicitudes HTTP a los métodos correspondientes.

  • Cliente REST: permite consumir enpoints HTTP. Bajo el capó, utiliza las funcionalidades de E/S no bloqueante de Quarkus.

  • Qute: el motor de plantillas de Qute expone una API reactiva para renderizar plantillas de manera no bloqueante.

Datos

  • Hibernate Reactive: una versión de Hibernate ORM que utiliza clientes asíncronos y no bloqueantes para interactuar con la base de datos.

  • Hibernate Reactive con Panache: proporciona soporte de registros y repositorios activos sobre Hibernate Reactive.

  • Cliente PostgreSQL reactivo: un cliente asíncrono y no bloqueante que interactúa con una base de datos PostgreSQL, permitiendo alta concurrencia.

  • Cliente MySQL reactivo: un cliente asíncrono y no bloqueante que interactúa con una base de datos MySQL

  • La extensión de MongoDB: expone APIs imperativas y reactivas (Mutiny) para interactuar con MongoDB.

  • Mongo con Panache: ofrece soporte de active record tanto para las APIs imperativas como reactivas.

  • La extensión de Cassandra: expone APIs imperativas y reactivas (Mutiny) para interactuar con Cassandra

  • La extensión de Redis: expone APIs imperativas y reactivas (Mutiny) para almacenar y recuperar datos desde un almacén clave-valor Redis.

Arquitectura dirigida por eventos

  • Mensajería Reactiva: permite implementar aplicaciones dirigidas por eventos utilizando código reactivo e imperativo.

  • Conector Kafka para mensajería reactiva: permite implementar aplicaciones que consumen y escriben registros en Kafka

  • Conector AMQP 1.0 para Mensajería Reactiva: permite implementar aplicaciones que envían y reciben mensajes AMQP.

Protocolos y utilidades de red

  • gRPC: permite implementar y consumir servicios gRPC. Ofrece interfaces de programación reactivas e imperativas.

  • GraphQL: permite implementar y consultar (como cliente) un almacén de datos utilizando GraphQL. Ofrece APIs Mutiny y suscripciones como flujos de eventos.

  • Tolerancia a fallos: proporciona capacidades de reintento, fallback y circuit breakers a su aplicación. Puede utilizarse con tipos de Mutiny.

Motor

  • Vert.x : el motor reactivo subyacente de Quarkus. La extensión permite acceder a la instancia administrada de Vert.x, así como a su variante Mutiny (que expone la API de Vert.x utilizando tipos de Mutiny)

  • Propagación de contexto: captura y propaga objetos contextuales (transacción, principal, etc.) dentro de una tubería reactiva

Related content