Comprendiendo las colas con RabbitMQ (I)
¡Buenas a todos! En este post veremos como funcionan las sistemas de colas y si encajan en nuestros proyectos.
En ocasiones, debemos tener en cuenta la delegación de tareas a terceros. La comunicación con sistemas internos o externos con transmisión de información, por lo que debemos asegurarnos que estos se transmiten correctamente, de una forma rápida y fiable. Las colas de mensajes surgen para estos cometidos actuando de intermediarios entre la comunicación entre sistemas.
Debido a esto, debemos considerar si nuestro proyecto necesita un gestor de colas o por el contrario, sería añadir mas dificultad y puntos de fallo a un proyecto que no necesita la comunicación con terceros internos o externos.
Empecemos por explicar que es RabbitMQ:
RabbitMQ es un software de negociación de mensajes de código abierto, y entra dentro de la categoría de middleware de mensajería. Implementa el estándar Advanced Message Queuing Protocol (AMQP).
En resumen, es un gestor de colas que nos proporciona la administración de mensajes entre sistemas, pudiendo así distribuir tareas de una forma más sencilla.
Mencionemos sus principales elementos:
- Un productor o Producing: es el que envía el mensaje.
- Cola o queue: donde los mensajes serán guardados de forma secuencial
- Cliente o Consuming: es el que recibe el mensaje.
La estructura de nuestro proyecto debe tener en cuenta trabajar de forma asíncrona con eventos para poder responder a cada nuevo mensaje.
Flujo de mensajes
Los mensajes estándar siguen este flujo:
- El productor o Producing genera el mensaje y lo envía a nuestro RabbitMQ.
- RabbitMQ recibe el mensaje y lo enruta hacia su cola correspondiente.
- El mensaje permanece en la cola hasta que el cliente o Consuming lo recibe.
- El cliente o Consuming procesa el mensaje.
El mensaje es considerado como recibido si el Consuming lo confirma o si la configuración de la cola no tiene confirmación este se da por recibido cuando llega al sistema.
Pongámoslo en práctica con un proyecto real
Nuestra idea de proyecto es ofrecer un servicio de generación de par de llaves encriptadas, pudiendo solicitar el tipo de llave que quieres y donde quieres recibirlo.
De primeras, pensaríamos en la solución más sencilla, un servidor web que reciba la petición, genere la llave y envíe el correo por cada petición, pero esta estructura tiene un gran problema, el tiempo de procesamiento por cada solicitud.
Si construimos un monolito, en el lenguaje que queramos, cuando haya un cierto número de peticiones simultáneas, solicitando unas llaves, con el tiempo de procesamiento de CPU que se necesita para su creación, colgaríamos nuestro servidor y no podríamos recibir más peticiones.
Si pensamos bien como funciona nuestro servicio, no es necesario crear la llave instantáneamente por cada petición, el cliente va a esperar su correo cuando esté lista y por tanto no debemos bloquear que el servidor web pueda seguir aceptando peticiones de otros clientes.
Despliegue e infraestructura
Para comenzar con nuestro proyecto crearemos una estructura de contenedores orquestados con Docker-compose como veremos a continuación:
RabbitMQ
Necesitaremos el gestor de colas en nuestro proyecto, yo he decidido por facilidad de instalación y despliegue montarlo en Docker.
En este caso es tan sencillo como buscar la imagen oficial de RabbitMQ en DockerHub
Lo podemos desplegar con docker-compose con el siguiente yaml:
Donde lo más destacable es:
- Utilizar la imagen oficial de RabbitMQ.
- Definir usuario y contraseña (Recordad tenerla oculta ;) ).
- Los puertos del protocolo AMQP y si queremos acceder a un panel web de RabbitMQ.
NodeJS
En este caso, utilizaremos nodeJS por la simplicidad del proyecto y por su fácil distribución, por ello crearemos nuestros Dockerfiles donde simplemente configuraremos nuestro servidor nodeJS.
Servidor web
Nuestro servidor web, que acepta las peticiones de generación de llaves, expondrá el puerto 8080 por lo que debemos tenerlo público en nuestro Dockerfile.
Generador de llaves
Nuestro generador de llaves no está expuesto al exterior por lo que no debemos abrir ningún puerto, únicamente copiamos el código, instalamos y ejecutamos.
Gestor de correos
Como ocurre con el anterior servidor este no está expuesto ya que simplemente es un cliente que hará uso de otro servicio de correo, en este caso gmail.
Nginx
Por último, tendremos un servidor web que alojará a nuestro sencillo cliente que servirá para que los usuarios puedan hacer las peticiones a nuestro Endpoint. En este caso la configuración de nuestro Dockerfile es únicamente introducir nuestro sitio web en el contenedor.
En el siguiente post nos adentraremos en las tripas del código detallando como manejamos esta estructura ¡Hasta la próxima!