Colas de Mensajes en la Arquitectura Moderna
Los microservicios se comunican asincrónicamente a través de colas de mensajes y plataformas de event streaming. En lugar de que el Servicio A llame directamente al Servicio B (HTTP sincrónico), el Servicio A publica un mensaje en una cola, y el Servicio B lo consume cuando está listo. Esto desacopla los servicios y mejora la resiliencia.
Dos tecnologías dominantes sirven este propósito:
RabbitMQ — Un message broker tradicional. Los mensajes se enrutan a través de exchanges hacia queues. Una vez que un consumidor confirma un mensaje, se elimina de la queue. Mejor para distribución de tareas y patrones request/reply.
Apache Kafka — Una plataforma distribuida de event streaming. Los mensajes (eventos) se agregan a particiones de topics y se retienen por un período configurable. Los consumidores rastrean su posición (offset) y pueden reproducir eventos. Mejor para event sourcing, data pipelines y streaming de alto throughput.
Conceptos de RabbitMQ para Testers
Exchanges, Queues y Bindings
Producer → Exchange → Binding → Queue → Consumer
| Tipo de Exchange | Comportamiento de Routing |
|---|---|
| Direct | Enruta a queues que coinciden exactamente con el routing key |
| Fanout | Transmite a todas las queues vinculadas |
| Topic | Enruta basado en coincidencia de patrones con wildcards |
| Headers | Enruta basado en atributos de headers del mensaje |
Escenarios Clave de Test para RabbitMQ
1. Entrega de mensajes:
- El producer envía un mensaje — ¿llega a la queue correcta?
- Mensaje con routing key “order.created” — ¿llega a “orders” queue pero no a “payments” queue?
2. Acknowledgment del consumidor:
- El consumidor procesa el mensaje y envía ACK — ¿se elimina el mensaje?
- El consumidor falla sin ACK — ¿se reentrega el mensaje?
- El consumidor envía NACK — ¿se reencola el mensaje o se envía a DLQ?
3. Dead letter queue:
- El mensaje falla 3 veces — ¿se mueve a la DLQ?
- ¿La DLQ contiene el mensaje original con metadata de falla?
4. TTL de mensaje:
- Mensaje con TTL de 60 segundos expira — ¿se elimina o se envía al dead letter?
import pika
import json
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost')
)
channel = connection.channel()
# Declarar queue con DLQ
channel.queue_declare(
queue='orders',
arguments={
'x-dead-letter-exchange': '',
'x-dead-letter-routing-key': 'orders.dlq',
'x-message-ttl': 60000,
}
)
channel.queue_declare(queue='orders.dlq')
# Publicar un mensaje
channel.basic_publish(
exchange='',
routing_key='orders',
body=json.dumps({'order_id': 123, 'amount': 99.99}),
properties=pika.BasicProperties(
delivery_mode=2,
content_type='application/json',
)
)
# Consumir y verificar
method, properties, body = channel.basic_get('orders', auto_ack=False)
assert method is not None, "No hay mensaje en la queue"
order = json.loads(body)
assert order['order_id'] == 123
channel.basic_ack(method.delivery_tag)
Conceptos de Kafka para Testers
Topics, Particiones y Consumer Groups
Producer → Topic (Partición 0, 1, 2, ...) → Consumer Group → Consumers
| Concepto | Descripción |
|---|---|
| Topic | Stream nombrado de eventos (como “orders”, “payments”) |
| Partición | Secuencia ordenada e inmutable de eventos dentro de un topic |
| Offset | Posición de un mensaje dentro de una partición |
| Consumer Group | Conjunto de consumidores que comparten la carga de un topic |
| Retención | Cuánto tiempo Kafka mantiene los mensajes |
Escenarios Clave de Test para Kafka
1. Producción de mensajes:
- El producer envía un evento — ¿aparece en el topic y partición correctos?
- Particionado por key — ¿eventos con la misma key siempre van a la misma partición?
2. Orden de mensajes:
- Eventos dentro de una partición mantienen orden — verifica produciendo A, B, C y consumiendo en el mismo orden.
- Eventos entre particiones no tienen garantía de orden.
3. Consumer groups:
- Dos consumidores en el mismo grupo — cada partición es consumida por exactamente un consumidor.
- Dos consumidores en grupos diferentes — ambos reciben todos los mensajes (patrón pub/sub).
4. Consumer lag:
- El producer envía 1,000 mensajes, el consumidor procesa 500 — el consumer lag debería ser 500.
5. Semántica exactly-once:
- Producer con escrituras idempotentes — duplicados resultan en un solo mensaje en el topic.
from kafka import KafkaProducer, KafkaConsumer
import json
producer = KafkaProducer(
bootstrap_servers='localhost:9092',
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
key_serializer=lambda k: k.encode('utf-8') if k else None,
)
producer.send(
'orders',
key='user-123',
value={'order_id': 456, 'status': 'created'}
)
producer.flush()
consumer = KafkaConsumer(
'orders',
bootstrap_servers='localhost:9092',
group_id='order-processor',
auto_offset_reset='earliest',
value_deserializer=lambda m: json.loads(m.decode('utf-8')),
consumer_timeout_ms=5000,
)
messages = []
for message in consumer:
messages.append(message.value)
if len(messages) >= 1:
break
assert messages[0]['order_id'] == 456
Testing de Serialización de Mensajes
Los mensajes deben serializarse (a bytes) y deserializarse correctamente. Formatos comunes:
| Formato | Pros | Contras |
|---|---|---|
| JSON | Legible, flexible | Sin schema enforcement, mayor tamaño |
| Avro | Schema enforced, compacto, soporta evolución | Requiere schema registry |
| Protobuf | Serialización rápida, tipado, compacto | Requiere generación de código |
Testing con Docker Compose
Configura infraestructura local de mensajería para testing:
services:
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.5.0
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
depends_on:
- zookeeper
Ejercicio: Laboratorio de Testing de Colas de Mensajes
Parte 1: Testing de RabbitMQ
Inicia RabbitMQ con Docker y prueba los siguientes escenarios:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
Tarea 1.1: Routing de exchange
Crea un topic exchange llamado “events” con dos queues:
- “orders_queue” vinculada con routing key “order.*”
- “payments_queue” vinculada con routing key “payment.*”
Publica mensajes y verifica el routing:
- Mensaje con key “order.created” → debe aparecer solo en orders_queue.
- Mensaje con key “payment.completed” → debe aparecer solo en payments_queue.
- Mensaje con key “user.registered” → no debe aparecer en ninguna queue.
Tarea 1.2: Dead letter queue
Configura la orders queue con una DLQ:
- Establece intentos máximos de entrega en 3.
- Publica un mensaje que tu consumidor intencionalmente rechace (NACK).
- Verifica que el mensaje se reintenta 3 veces y luego se mueve a la DLQ.
- Inspecciona el mensaje en la DLQ — ¿contiene el payload original y metadata del rechazo?
Tarea 1.3: Persistencia de mensajes
- Publica 10 mensajes en una queue durable con delivery mode persistente.
- Reinicia el container de RabbitMQ.
- Después del reinicio, verifica que los 10 mensajes siguen en la queue.
Parte 2: Testing de Kafka
Inicia Kafka con Docker Compose y prueba:
Tarea 2.1: Orden por partición
- Crea un topic “test-orders” con 3 particiones.
- Produce 10 eventos con key “user-A” y 10 con key “user-B”.
- Consume todos los eventos y verifica:
- Todos los eventos “user-A” están en orden relativo entre sí.
- Todos los eventos “user-B” están en orden relativo entre sí.
- Los eventos pueden intercalarse entre usuarios (diferentes particiones).
Tarea 2.2: Consumer groups
- Inicia dos consumidores en el mismo grupo (“order-processors”).
- Produce 100 mensajes a un topic con 3 particiones.
- Verifica que cada consumidor procesa un subconjunto de mensajes.
- Detén un consumidor y verifica que el otro toma todas las particiones (rebalanceo).
Tarea 2.3: Monitoreo de consumer lag
- Produce 1,000 mensajes a un topic.
- Inicia un consumidor lento (con 100ms de delay por mensaje).
- Usa
kafka-consumer-groups.sh --describepara monitorear el consumer lag. - Registra cómo el lag cambia con el tiempo mientras el consumidor se pone al día.
Parte 3: Reporte Comparativo
Después de completar ambas partes, escribe una comparación:
| Criterio | RabbitMQ | Kafka |
|---|---|---|
| Retención de mensajes | Eliminado después de ACK | Retenido por política de retención |
| Garantía de orden | FIFO por queue | Orden por partición |
| Capacidad de replay | No (mensaje eliminado) | Sí (consumidor resetea offset) |
| Throughput | Miles/seg | Millones/seg |
| Mejor caso de uso | Distribución de tareas, RPC | Event streaming, data pipelines |
Incluye tus observaciones sobre complejidad de testing, esfuerzo de configuración y herramientas de debugging para cada plataforma.