Este proyecto es una Prueba de Concepto (POC) diseñada para una demostración con el equipo. Demuestra cómo los sistemas desacoplados pueden comunicarse de manera agnóstica al lenguaje utilizando RabbitMQ como un intermediario de mensajes (message broker).
El taller incluye ejemplos de Productores (quienes envían mensajes) y Consumidores (quienes reciben mensajes) implementados tanto en Node.js como en Python.
El código ha sido refactorizado en dos directorios principales para mayor claridad:
js/: Contiene los ejemplos de productor y consumidor en JavaScript (Node.js).python/: Contiene los ejemplos de productor y consumidor en Python.
Todos los ejemplos están configurados para usar una única cola de RabbitMQ definida en el archivo .env, lo que demuestra la interoperabilidad entre diferentes lenguajes.
La arquitectura de colas de mensajes es fundamental para construir sistemas resilientes y escalables.
- Agnóstico al Lenguaje: Como demuestra este taller, un productor en Python puede enviar un mensaje que es consumido por un servicio en Node.js, y viceversa. Los sistemas no necesitan saber en qué tecnología están construidos los demás.
- Resiliencia: Si un consumidor se cae o está en mantenimiento, los mensajes del productor se acumulan de forma segura en la cola de RabbitMQ. Una vez que el consumidor vuelve a estar en línea, puede procesar los mensajes pendientes sin que se pierda información.
- Escalabilidad: Si un productor envía más mensajes de los que un solo consumidor puede procesar, se pueden levantar múltiples instancias del consumidor. RabbitMQ distribuirá la carga de mensajes entre todas las instancias activas.
- Flexibilidad: Se pueden agregar nuevos consumidores a una cola existente para realizar nuevas tareas (como análisis de datos, auditoría, etc.) sin tener que modificar el productor original.
RabbitMQ ofrece diferentes tipos de colas, cada una con características específicas para distintas necesidades.
Son las colas tradicionales y el tipo por defecto. Ofrecen un modelo FIFO (First-In-First-Out) y son ideales para una amplia variedad de casos de uso.
- Beneficios:
- Alto Rendimiento: Optimizadas para un alto volumen de mensajes y baja latencia.
- Flexibilidad: Soportan funcionalidades como mensajes persistentes, TTL (Time-To-Live) y límites de longitud.
- Ejemplo de declaración (Python con Pika):
channel.queue_declare(queue='classic_queue', durable=True)
Son una implementación moderna enfocada en la alta disponibilidad y la seguridad de los datos. Utilizan el algoritmo de consenso Raft para replicar los mensajes a través de múltiples nodos del clúster.
- Beneficios:
- Alta Disponibilidad: Garantizan que la cola permanezca operativa incluso si algunos nodos fallan.
- Seguridad de Datos: Los mensajes se replican, lo que reduce significativamente el riesgo de pérdida de datos.
- Manejo de Mensajes "Envenenados": Tienen mecanismos para gestionar mensajes que causan fallos repetidos en los consumidores.
- Ejemplo de declaración (Python con Pika):
channel.queue_declare(queue='quorum_queue', durable=True, arguments={'x-queue-type': 'quorum'})
Diseñadas para escenarios de alto rendimiento que manejan flujos masivos de eventos, como telemetría o logs. Actúan como un log de solo anexo (append-only).
- Beneficios:
- Rendimiento Extremo: Optimizadas para procesar millones de mensajes por segundo.
- Replay de Mensajes: Los consumidores pueden leer y releer mensajes desde cualquier punto del flujo, similar a Apache Kafka.
- Eficiencia de Recursos: Utilizan el disco de manera eficiente, lo que reduce la presión sobre la memoria.
- Ejemplo de declaración (Python con Pika):
channel.queue_declare(queue='stream_queue', durable=True, arguments={'x-queue-type': 'stream'})
Un exchange es el responsable de recibir los mensajes de los productores y enrutarlos a las colas correctas. El tipo de exchange determina cómo se realiza este enrutamiento.
Enruta los mensajes a las colas cuyo binding key coincide exactamente con la routing key del mensaje. Es ideal para enrutamiento unicast (un solo destino).
- Caso de uso: Enviar una tarea específica a un worker concreto.
Ignora la routing key y envía una copia de cada mensaje a todas las colas que están vinculadas a él.
- Caso de uso: Distribuir notificaciones o actualizaciones de estado a múltiples servicios.
Enruta los mensajes a las colas basándose en una coincidencia de patrones entre la routing key y el patrón de binding. Se usan comodines como * (coincide con una palabra) y # (coincide con cero o más palabras).
- Caso de uso: Enviar logs con diferentes niveles de severidad (
error,info,warning) a distintos consumidores. Por ejemplo, un patrónlogs.*recibiría todos los logs, mientras quelogs.errorsolo recibiría los de error.
Utiliza los atributos de las cabeceras (headers) del mensaje para el enrutamiento, en lugar de la routing key. Permite crear reglas de enrutamiento más complejas.
- Caso de uso: Enrutar mensajes basándose en metadatos como el tipo de contenido o el origen del mensaje.
A continuación, se presentan ejercicios prácticos basados en los tutoriales oficiales de RabbitMQ para reforzar los conceptos.
Este ejercicio es la introducción más simple a RabbitMQ. Aprenderás a:
- Enviar un único mensaje a una cola.
- Recibir ese mensaje en un consumidor.
Es el punto de partida para entender la dinámica básica de productor-consumidor.
Este ejercicio demuestra cómo distribuir tareas que consumen tiempo entre múltiples consumidores. Aprenderás a:
- Enviar tareas a una única cola.
- Hacer que varios consumidores compartan la carga de trabajo de esa cola.
- Asegurar que los mensajes sean confirmados (acknowledged) después de ser procesados.
En este ejercicio, aprenderás a enviar un mensaje a múltiples consumidores a la vez utilizando un exchange de tipo fanout.
- Un productor envía un mensaje a un exchange.
- El exchange distribuye el mensaje a todas las colas vinculadas a él.
- Cada consumidor conectado a su propia cola recibe una copia del mensaje.
Este ejercicio avanzado demuestra el poder de las RabbitMQ Streams para escenarios de alta ingesta de datos, como la actualización masiva de inventario en un e-commerce.
A diferencia de las colas tradicionales, los streams son logs de mensajes de solo anexo (append-only), optimizados para un rendimiento extremo y la capacidad de "reproducir" mensajes.
- Productor: Simulará un sistema que genera miles de actualizaciones de inventario (ej. "SKU-123, +10", "SKU-456, -5") y las envía a un stream llamado
inventory_updates. - Consumidores:
- Un consumidor (ej. un servicio de base de datos) leerá el stream desde el principio para aplicar las actualizaciones.
- Otro consumidor (ej. un servicio de analítica) podrá conectarse más tarde y leer el mismo stream desde un punto específico en el tiempo (offset) para analizar los movimientos de inventario sin afectar al primer consumidor.
Este patrón es ideal para sistemas de event sourcing, telemetría o cualquier caso de uso que requiera procesar un gran volumen de eventos de forma resiliente y en tiempo real.
- Docker y Docker Compose
- Node.js y npm
- Python 3 y uv
Este es el primer y más importante paso. En la raíz del proyecto, ejecuta:
docker-compose up -dEsto iniciará un contenedor de RabbitMQ en segundo plano.
- Interfaz de Administración: Puedes monitorear las colas y los mensajes en http://localhost:15672.
- Credenciales:
user/password.
Necesitarás dos terminales.
Terminal 1: Consumidor (Node.js) Este script se conectará a RabbitMQ y esperará mensajes.
cd js/consumer
npm install
npm startTerminal 2: Productor (Node.js) Este script enviará un único mensaje a la cola y terminará.
cd js/producer
npm install
npm startVerás en la terminal del consumidor que el mensaje enviado desde el productor ha sido recibido y procesado.
Primero, asegurate de copiar el archivo .env.example a .env y reemplazar los valores que hagan falta. También necesitarás dos terminales para los ejemplos de Python.
Terminal 3: Consumidor (Python) Este script se conectará y esperará mensajes en la misma cola.
cd python/consumer
# Crear entorno virtual y activar
uv venv
source .venv/bin/activate
# Instalar dependencias
uv sync
# Ejecutar el consumidor
python consumer.pyTerminal 4: Productor (Python) Este script enviará un mensaje a la cola.
cd python/producer
# Crear entorno virtual y activar
uv venv
source .venv/bin/activate
# Instalar dependencias
uv sync
# Ejecutar el productor
python producer.pyPuedes mezclar los productores y consumidores:
- Ejecuta el productor de Python y observa cómo el consumidor de Node.js recibe el mensaje.
- Ejecuta el productor de Node.js y observa cómo el consumidor de Python recibe el mensaje.
Esto demuestra que ambos sistemas se comunican a través de la cola de RabbitMQ sin necesidad de conocer los detalles de implementación del otro.