Lección 2 de 314 min
Colas y mensajería: RabbitMQ, SQS y SNS
Colas frente a publicación/suscripción, garantías de entrega, colas de mensajes fallidos e idempotencia.
Cuando un cliente paga un pedido, tu sistema tiene que cobrar, emitir la factura, avisar al almacén y enviar un correo de confirmación. Si todo eso ocurre dentro de la misma petición HTTP, el cliente espera a que terminen todos los pasos, y basta con que el servicio de correo esté caído para que el pago entero falle.
La alternativa es dejar un mensaje («el pedido 42 está pagado») en un sistema intermedio y responder al cliente enseguida. Otros procesos recogen ese mensaje a su ritmo y, si alguno falla, lo reintentan más tarde. En esta lección verás las dos formas básicas de repartir mensajes, qué garantías de entrega puedes esperar y tres servicios muy usados: RabbitMQ, Amazon SQS y Amazon SNS.
Dos formas de repartir mensajes
En ambos casos hay productores, que envían mensajes, y consumidores, que los procesan, sin necesidad de conocerse ni de estar activos a la vez. La diferencia está en cuántos consumidores reciben cada mensaje.
Cola punto a punto
Cada mensaje lo procesa un único consumidor, aunque haya varios escuchando.
Una cola funciona como la fila de una panadería con varios dependientes: cada cliente lo atiende uno solo, el primero que queda libre. Los productores dejan mensajes al final y los consumidores los van sacando. Si añades más consumidores, el trabajo se reparte entre ellos y la cola se vacía antes.
Los mensajes esperan en la cola aunque no haya ningún consumidor activo. Eso absorbe los picos: si llegan mil pedidos en un minuto, la cola crece y los consumidores la vacían a su velocidad, sin tumbar a nadie.
Úsalo cuando
Para repartir trabajo entre varios procesos: redimensionar imágenes, generar PDFs, enviar correos, cualquier tarea que deba hacerse una vez.
Ten en cuenta
Con varios consumidores en paralelo, el orden de procesamiento no está garantizado aunque la cola lo esté. Si el orden importa, necesitas una cola con orden por grupo (como SQS FIFO) o un único consumidor.
Publicación/suscripción
Cada mensaje llega a todos los suscriptores interesados, cada uno con su copia.
En el modelo publish/subscribe (pub/sub), el productor publica un evento en un tema (topic) sin saber quién lo escucha. Cada suscriptor recibe su propia copia: facturación, almacén y correo se enteran del mismo «pedido pagado» y actúan cada uno por su cuenta.
Su gran ventaja es el desacoplamiento: para que un nuevo servicio reaccione a los pedidos, basta con suscribirlo al tema, sin tocar el código del productor.
Úsalo cuando
Cuando un mismo hecho interesa a varios sistemas independientes: notificar eventos de negocio, invalidar cachés en varias máquinas, alimentar analítica.
Ten en cuenta
En muchos sistemas pub/sub, si un suscriptor no está escuchando en ese momento, se pierde el mensaje. Lo habitual es suscribir una cola por consumidor para que los mensajes esperen: es el patrón fan-out que verás con SNS y SQS.
Garantías de entrega
Las redes fallan y los procesos se caen a mitad de trabajo. Cada sistema de mensajería elige qué prefiere arriesgar: perder algún mensaje o entregarlo más de una vez.
Como mucho una vez (at-most-once)
Nunca hay duplicados, pero algún mensaje se puede perder por el camino.
El mensaje se da por entregado en cuanto sale hacia el consumidor. Si el consumidor se cae antes de procesarlo, nadie lo reintenta y ese mensaje desaparece. Es la garantía más barata: no hay confirmaciones ni reintentos.
Úsalo cuando
Cuando perder un mensaje suelto no importa porque llegará otro enseguida: la posición GPS de un repartidor cada segundo, métricas de uso, registros de depuración.
Ten en cuenta
Nunca para nada que tenga valor por sí mismo: un pago, un pedido o un correo de restablecimiento de contraseña.
Al menos una vez (at-least-once)
Ningún mensaje se pierde, a cambio de que alguno llegue repetido.
El sistema guarda el mensaje hasta que el consumidor confirma (ack) que lo ha procesado. Si la confirmación no llega, porque el consumidor se cayó o tardó demasiado, el mensaje se vuelve a entregar. Es la garantía por defecto de RabbitMQ con confirmaciones manuales y de las colas estándar de SQS.
El precio es que un mensaje puede procesarse dos veces: por ejemplo, si el consumidor terminó el trabajo pero se cayó justo antes de confirmar.
Úsalo cuando
En casi todos los procesos de negocio: es la opción sensata siempre que los consumidores sepan manejar duplicados.
Ten en cuenta
Confirma el mensaje después de procesarlo, no antes; si confirmas al recibirlo, vuelves a tener at-most-once sin darte cuenta.
«Exactamente una vez» en la práctica
Al menos una vez más un consumidor idempotente: el duplicado llega, pero no hace daño.
Garantizar que un mensaje se entregue exactamente una vez a través de una red que falla es, en general, imposible. Lo que sí puedes conseguir es que el efecto ocurra una sola vez: recibir el mensaje al menos una vez y hacer que el consumidor sea idempotente, es decir, que procesar el mismo mensaje dos veces dé el mismo resultado que hacerlo una.
La técnica habitual es dar a cada mensaje un identificador único y guardar los ya procesados. Algunos servicios ayudan, como la deduplicación de SQS FIFO, pero solo dentro de ciertos límites: la idempotencia del consumidor sigue siendo tu responsabilidad.
Úsalo cuando
Siempre que un duplicado cause un problema real: cobrar dos veces, descontar el stock dos veces, enviar dos paquetes.
Ten en cuenta
Guarda el «ya procesado» en la misma transacción que el efecto. Si lo apuntas en otra base de datos, un fallo entre las dos escrituras vuelve a abrir la puerta al duplicado.
async function procesarPago(mensaje) {
await db.transaction(async (tx) => {
// Si el id ya existe, ON CONFLICT hace que no se inserte nada
const nuevo = await tx.query(
"INSERT INTO mensajes_procesados (id) VALUES ($1) ON CONFLICT DO NOTHING",
[mensaje.id],
);
if (nuevo.rowCount === 0) return; // duplicado: ya se cobró
await tx.query("UPDATE pedidos SET pagado = true WHERE id = $1", [mensaje.pedidoId]);
});
}Cola de mensajes fallidos (DLQ)
Donde acaban los mensajes que fallan una y otra vez, para revisarlos aparte.
Con at-least-once, un mensaje que siempre falla (un JSON mal formado, un pedido que ya no existe) se reintentaría para siempre, gastando recursos y, en algunas colas, bloqueando a los que vienen detrás. La solución es una dead-letter queue: tras un número máximo de intentos, el mensaje se aparta a otra cola.
Allí no molesta a nadie y puedes inspeccionarlo, corregir el fallo y devolverlo a la cola original. Tanto SQS como RabbitMQ lo ofrecen; en RabbitMQ se llama dead letter exchange.
Úsalo cuando
En cualquier cola de producción. Configúrala desde el primer día.
Ten en cuenta
Una DLQ que nadie mira es un cementerio de pedidos perdidos. Crea una alarma que avise en cuanto tenga algún mensaje.
Atención
Si alguien te promete entrega «exactamente una vez», pregunta qué ocurre cuando el consumidor se cae justo después de hacer el trabajo y antes de confirmarlo. La respuesta casi siempre acaba siendo: «diseña el consumidor para que sea idempotente».
RabbitMQ, Amazon SQS y Amazon SNS
RabbitMQ
Un broker de mensajes de código abierto con enrutamiento flexible mediante exchanges.
- Protocolo:
- AMQP
- Licencia:
- Código abierto
RabbitMQ es un broker, un servidor que recibe mensajes y los reparte. Su protocolo principal es AMQP 0-9-1, con clientes para casi cualquier lenguaje, y las versiones recientes también hablan AMQP 1.0 de forma nativa. Lo instalas tú, en tus máquinas o en contenedores, o lo contratas como servicio gestionado.
Los productores no escriben directamente en colas, sino en un exchange, que decide a qué colas va cada mensaje según sus bindings (reglas de enlace) y la routing key del mensaje. Un exchange direct envía a las colas cuya clave coincide exactamente; uno topic admite patrones, donde * es una palabra y # cero o más; y uno fanout copia el mensaje a todas sus colas, es decir, pub/sub.
Los consumidores confirman cada mensaje con un ack. Si un consumidor se desconecta sin confirmar, RabbitMQ entrega el mensaje a otro.
Úsalo cuando
Cuando necesitas enrutar mensajes con reglas ricas entre muchos servicios, no dependes de un proveedor de nube concreto o tu infraestructura vive en tus propios servidores.
Ten en cuenta
Para no perder mensajes si el broker se reinicia, declara colas durables (en las versiones recientes se recomiendan las quorum queues, replicadas) y publica mensajes persistentes. Y operar un clúster de RabbitMQ es trabajo: alguien tiene que vigilarlo y actualizarlo.
const canal = await (await amqp.connect("amqp://localhost")).createChannel();
await canal.assertExchange("pedidos", "topic", { durable: true });
await canal.assertQueue("facturacion", { durable: true });
// Recibe «pedido.mx.pagado», «pedido.es.pagado»…
await canal.bindQueue("facturacion", "pedidos", "pedido.*.pagado");
canal.publish("pedidos", "pedido.mx.pagado",
Buffer.from(JSON.stringify({ pedidoId: 42 })), { persistent: true });
await canal.consume("facturacion", async (msg) => {
await emitirFactura(JSON.parse(msg.content.toString()));
canal.ack(msg); // confirma solo cuando ha terminado
});Amazon SQS
Colas gestionadas de AWS: sin servidores que mantener, pagas por uso.
- Tipos:
- Estándar y FIFO
- Modelo:
- Punto a punto
Amazon Simple Queue Service te da colas punto a punto sin instalar nada. Hay dos tipos: las estándar, con entrega al menos una vez, un orden aproximado y un rendimiento casi ilimitado; y las FIFO (su nombre acaba en .fifo), que mantienen el orden dentro de cada grupo de mensajes y eliminan duplicados durante un intervalo.
Al recibir un mensaje, SQS no lo borra: lo oculta durante el visibility timeout. Si el consumidor lo borra antes de que acabe ese plazo, desaparece; si no, vuelve a aparecer para otro consumidor. Con long polling (WaitTimeSeconds), la petición de lectura espera hasta que llegue algo en lugar de volver vacía, lo que ahorra peticiones y dinero.
Una redrive policy con maxReceiveCount manda a una cola de mensajes fallidos lo que se ha recibido demasiadas veces sin borrarse.
Úsalo cuando
Cuando ya estás en AWS y quieres una cola fiable sin operarla, sobre todo con Lambda, que puede leer de SQS directamente.
Ten en cuenta
Ajusta el visibility timeout a lo que tarda de verdad tu proceso: si es más corto, otro consumidor recibirá el mismo mensaje mientras el primero sigue trabajando. Y SQS no enruta: cada cola es un destino, sin exchanges ni patrones.
const sqs = new SQSClient({});
const QueueUrl = process.env.COLA_PEDIDOS;
while (true) {
const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({
QueueUrl,
MaxNumberOfMessages: 10,
WaitTimeSeconds: 20, // long polling
VisibilityTimeout: 60, // tiempo para procesarlo antes de que reaparezca
}));
for (const m of Messages) {
await procesar(JSON.parse(m.Body));
await sqs.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: m.ReceiptHandle }));
}
}Amazon SNS
Temas pub/sub gestionados de AWS: publicas una vez y llega a todos los suscriptores.
- Modelo:
- Publicación/suscripción
- Suscriptores:
- SQS, Lambda, HTTP, email…
Amazon Simple Notification Service organiza los mensajes en temas (topics). Cada tema tiene suscriptores de distintos tipos: colas SQS, funciones Lambda, endpoints HTTP/S, direcciones de correo o SMS, entre otros. Al publicar, SNS entrega una copia a cada uno.
El patrón estrella es el fan-out SNS → SQS: suscribes una cola SQS por servicio interesado. SNS reparte el evento y cada cola lo guarda hasta que su consumidor lo procese, con sus propios reintentos y su DLQ. Con filtros de suscripción, cada cola recibe solo los mensajes cuyos atributos le interesan.
Úsalo cuando
Para avisar a varios sistemas del mismo evento o para enviar notificaciones a personas (correo, SMS, push) desde AWS.
Ten en cuenta
SNS no guarda los mensajes para suscriptores futuros: si publicas en un tema sin suscriptores, el mensaje se pierde. Y la cola SQS necesita una política de acceso que permita a SNS escribir en ella.
aws sns create-topic --name pedido-pagado
# Dos colas suscritas al mismo tema ($TEMA y $COLA_… son ARN)
aws sns subscribe --topic-arn "$TEMA" --protocol sqs \
--notification-endpoint "$COLA_FACTURACION"
aws sns subscribe --topic-arn "$TEMA" --protocol sqs \
--notification-endpoint "$COLA_ALMACEN"
# Una sola publicación: llega a las dos colas
aws sns publish --topic-arn "$TEMA" --message '{"pedidoId":42}'¿Sabías que…?
En AWS también existe EventBridge, un bus de eventos con reglas de enrutado más ricas que SNS. Y si lo que necesitas es guardar un historial de eventos que varios consumidores leen a su ritmo, la herramienta habitual es Apache Kafka, que funciona como un registro de eventos más que como una cola.
Interactivo
RabbitMQ, SQS y SNS cara a cara
Activa o desactiva opciones para compararlas cara a cara.
| Característica | RabbitMQ | Amazon SQS | Amazon SNS |
|---|---|---|---|
| Modelo | Colas y pub/sub mediante exchanges | Cola punto a punto | Pub/sub con temas |
| Quién lo opera | Tú, o un servicio gestionado | AWS | AWS |
| Enrutamiento | Rico: direct, topic, fanout | Ninguno: cada cola es un destino | Filtros en cada suscripción |
| Orden | Por cola, si hay un solo consumidor | Estándar: aproximado; FIFO: por grupo | Solo en temas FIFO |
| Si nadie está leyendo | El mensaje espera en su cola | El mensaje espera hasta su periodo de retención | No guarda nada: sin suscriptores, se pierde |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Los usuarios suben fotos y hay que generar tres tamaños de miniatura de cada una. Tienes cinco procesos trabajadores y cada foto debe procesarla solo uno.
Para recordar
- En una cola punto a punto cada mensaje lo procesa un consumidor; en pub/sub, cada suscriptor recibe su copia.
- Lo normal es la entrega al menos una vez: confirma después de procesar y haz idempotentes a tus consumidores.
- «Exactamente una vez» en la práctica es al menos una vez más idempotencia.
- Configura siempre una cola de mensajes fallidos y vigílala.
- RabbitMQ enruta con exchanges y lo operas tú; SQS es una cola gestionada; SNS reparte a varios suscriptores, y SNS → SQS es el fan-out clásico de AWS.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.Un consumidor de SQS tarda unos 90 segundos en procesar cada mensaje, pero el visibility timeout es de 30. ¿Qué ocurrirá?
2.Si tu sistema entrega «al menos una vez», ¿qué debe cumplir el consumidor?
3.Una cola de RabbitMQ está enlazada a un exchange
topiccon el patrónpedido.*.pagado. ¿Qué routing key llega a la cola?4.Publicas un mensaje en un tema de SNS que todavía no tiene suscriptores. ¿Qué pasa con él?
5.¿Para qué sirve el long polling al leer de SQS?