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 ExchangeComportamiento de Routing
DirectEnruta a queues que coinciden exactamente con el routing key
FanoutTransmite a todas las queues vinculadas
TopicEnruta basado en coincidencia de patrones con wildcards
HeadersEnruta 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
ConceptoDescripción
TopicStream nombrado de eventos (como “orders”, “payments”)
ParticiónSecuencia ordenada e inmutable de eventos dentro de un topic
OffsetPosición de un mensaje dentro de una partición
Consumer GroupConjunto de consumidores que comparten la carga de un topic
RetenciónCuá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:

FormatoProsContras
JSONLegible, flexibleSin schema enforcement, mayor tamaño
AvroSchema enforced, compacto, soporta evoluciónRequiere schema registry
ProtobufSerialización rápida, tipado, compactoRequiere 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:

  1. Mensaje con key “order.created” → debe aparecer solo en orders_queue.
  2. Mensaje con key “payment.completed” → debe aparecer solo en payments_queue.
  3. Mensaje con key “user.registered” → no debe aparecer en ninguna queue.

Tarea 1.2: Dead letter queue

Configura la orders queue con una DLQ:

  1. Establece intentos máximos de entrega en 3.
  2. Publica un mensaje que tu consumidor intencionalmente rechace (NACK).
  3. Verifica que el mensaje se reintenta 3 veces y luego se mueve a la DLQ.
  4. Inspecciona el mensaje en la DLQ — ¿contiene el payload original y metadata del rechazo?

Tarea 1.3: Persistencia de mensajes

  1. Publica 10 mensajes en una queue durable con delivery mode persistente.
  2. Reinicia el container de RabbitMQ.
  3. 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

  1. Crea un topic “test-orders” con 3 particiones.
  2. Produce 10 eventos con key “user-A” y 10 con key “user-B”.
  3. 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

  1. Inicia dos consumidores en el mismo grupo (“order-processors”).
  2. Produce 100 mensajes a un topic con 3 particiones.
  3. Verifica que cada consumidor procesa un subconjunto de mensajes.
  4. Detén un consumidor y verifica que el otro toma todas las particiones (rebalanceo).

Tarea 2.3: Monitoreo de consumer lag

  1. Produce 1,000 mensajes a un topic.
  2. Inicia un consumidor lento (con 100ms de delay por mensaje).
  3. Usa kafka-consumer-groups.sh --describe para monitorear el consumer lag.
  4. 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:

CriterioRabbitMQKafka
Retención de mensajesEliminado después de ACKRetenido por política de retención
Garantía de ordenFIFO por queueOrden por partición
Capacidad de replayNo (mensaje eliminado)Sí (consumidor resetea offset)
ThroughputMiles/segMillones/seg
Mejor caso de usoDistribución de tareas, RPCEvent streaming, data pipelines

Incluye tus observaciones sobre complejidad de testing, esfuerzo de configuración y herramientas de debugging para cada plataforma.