Mensajería y streaming de eventos
Middleware orientado a mensajes: colas, topics y comunicación asíncrona
Principiante12 min de lecturaActualizado
Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés
El middleware orientado a mensajes permite que las aplicaciones intercambien datos a través de un broker en lugar de llamarse directamente. Al terminar esta guía sabrás explicar los modelos punto a punto y publish-subscribe, identificar las partes de un mensaje, comparar los principales protocolos y brokers, y valorar cuándo la mensajería asíncrona es el estilo de integración adecuado para un problema.
Lo que aprenderás
- Comparar los cuatro estilos de integración y explicar qué aporta la mensajería frente a la transferencia de ficheros, la base de datos compartida y las llamadas remotas
- Describir la anatomía de un mensaje y los papeles del productor, el broker, el destino y el consumidor
- Distinguir las colas punto a punto de los topics publish-subscribe y elegir el adecuado para cada caso de uso
- Explicar las tres formas de desacoplamiento que proporciona un broker y lo que cuestan
- Situar JMS, AMQP, STOMP, MQTT y el protocolo de Kafka unos respecto a otros
- Elegir entre ActiveMQ, RabbitMQ y Kafka para una carga de trabajo concreta
Dos aplicaciones pueden compartir datos intercambiando ficheros, leyendo la misma base de datos, llamándose por la red o enviándose mensajes a través de un intermediario. Esta última opción —el middleware orientado a mensajes (MOM)— es la que permite que cada parte siga funcionando cuando la otra está lenta, saturada o caída. Esa propiedad es lo que convierte la mensajería en la columna vertebral del procesamiento de pedidos, los pipelines de pago, la recogida de logs y la mayoría de las arquitecturas orientadas a eventos. Esta guía explica el modelo que comparten todos los brokers, para que las guías siguientes sobre JMS, ActiveMQ, RabbitMQ y Kafka se lean como variaciones sobre un mismo tema y no como cuatro productos sin relación.
Cuatro estilos de integración, un compromiso
Todo problema de integración se reduce a mover datos y desencadenar comportamiento entre sistemas que no se escribieron juntos. La clasificación clásica distingue cuatro estilos, y cada uno corrige una debilidad del anterior a costa de introducir otra.
| Estilo | Cómo funciona | Punto fuerte | Punto débil |
|---|---|---|---|
| Transferencia de ficheros | Un sistema escribe un fichero y otro lo lee más tarde | Sencillo, sin runtime compartido, cualquier lenguaje | Latencia de horas, deriva del formato, sin retroalimentación |
| Base de datos compartida | Varias aplicaciones leen y escriben el mismo esquema | Datos siempre coherentes y actualizados | El esquema se convierte en un contrato que nadie puede cambiar; contención |
| Llamada a procedimiento remoto (HTTP, gRPC, RMI) | Una aplicación llama a una función expuesta por otra | Respuesta inmediata, modelo de programación conocido | Quien llama se bloquea; ambas partes deben estar activas; fallos en cascada |
| Mensajería | Las aplicaciones entregan pequeños paquetes a un broker que los reparte | Asíncrona, fiable, desacoplada | Consistencia eventual, depuración más difícil, más piezas móviles |
La mensajería no es «mejor» que las llamadas remotas. Es un contrato distinto: el emisor solo sabe que el broker aceptó el mensaje, no que alguien lo haya procesado. Por eso los sistemas basados en mensajería tienen que asumir que el trabajo ocurre más tarde y que los resultados llegan como nuevos mensajes y no como valores de retorno.
Anatomía de un mensaje
Sea cual sea el broker, un mensaje tiene las mismas tres partes.
- Las cabeceras (headers) son campos que el propio broker entiende y sobre los que puede actuar: un identificador único, una marca de tiempo, un destino, una prioridad, una caducidad, un modo de entrega (¿debe el broker escribirlo en disco antes de confirmarlo?) y, para request-reply, una dirección de respuesta y un identificador de correlación.
- Las propiedades (que RabbitMQ y Kafka también llaman headers) son pares clave-valor definidos por la aplicación. Los consumidores pueden filtrar por ellas, los enrutadores pueden decidir en función de ellas y los sistemas de tracing propagan en ellas los identificadores de traza. Son el sitio para metadatos como
tenant,eventTypeoschemaVersion. - El cuerpo (payload) es, para el broker, una secuencia opaca de bytes. JSON es la codificación más habitual hoy; los formatos binarios como Avro o Protocol Buffers se prefieren con volúmenes altos porque ocupan menos y llevan un esquema.
Una buena disciplina es poner todo lo que un enrutador o un filtro necesite en las cabeceras o las propiedades, y no obligar nunca a nadie a analizar el cuerpo para decidir adónde va un mensaje. Los brokers no pueden mirar dentro del cuerpo, y el código que lo hace queda acoplado al formato del payload.
{
"headers": {
"messageId": "ID:app-1-45231-1725700000000-1:1:1:1",
"timestamp": 1725700000000,
"deliveryMode": "PERSISTENT",
"replyTo": null,
"correlationId": null
},
"properties": {
"eventType": "OrderPlaced",
"schemaVersion": "2",
"tenant": "eu-west"
},
"body": "{\"orderId\":\"o-9911\",\"total\":149.90,\"currency\":\"EUR\"}"
}
Los identificadores de la cabecera son los que permiten a un consumidor detectar duplicados y los que vinculan una respuesta con su petición; puedes generar los tuyos con un generador de UUID cuando el broker no los asigna.
Productores, consumidores, destinos y el broker
El vocabulario es reducido. Un productor (publisher, emisor) crea un mensaje y se lo entrega al broker. El broker lo guarda en un destino y lo entrega a uno o varios consumidores (suscriptores, receptores). El productor nunca sabe quiénes son los consumidores, y los consumidores nunca saben quién produjo el mensaje: ambos solo conocen el nombre del destino.
El broker es el componente que hace fiable todo esto. Acepta mensajes aunque no haya ningún consumidor conectado, los persiste si se le pide, los vuelve a entregar cuando un consumidor falla y controla quién puede enviar a un destino o leer de él. También es un punto único de monitorización: la profundidad de las colas, el número de consumidores y el throughput se ven en un solo lugar.
No todo sistema de mensajería necesita un broker. Bibliotecas sin broker como ZeroMQ entregan directamente entre procesos y ganan en latencia, pero entonces el mensaje no tiene dónde esperar mientras el receptor está caído. En cuanto necesitas una cola que compartan varias aplicaciones y que sobreviva a los reinicios, necesitas un broker.
Dos dominios de mensajería: colas y topics
Los destinos vienen en dos variantes, y elegir la correcta es la primera decisión de diseño de cualquier sistema de mensajería.
Punto a punto: la cola
Una cola (queue) entrega cada mensaje a exactamente un consumidor. Si diez consumidores leen de la misma cola, el broker reparte los mensajes entre ellos —un patrón llamado competing consumers—, y así es como se escala el throughput horizontalmente: añades un consumidor y la cola se vacía más deprisa. Los mensajes esperan en la cola hasta que alguien los lee, así que productor y consumidor no tienen por qué estar en marcha a la vez. El procesamiento de pedidos, los trabajos de redimensionado de imágenes y el envío de correos son cargas típicas de cola: cada unidad de trabajo debe hacerse una vez.
Publish-subscribe: el topic
Un topic entrega cada mensaje a todos los suscriptores activos. Un único evento «dirección del cliente modificada» puede llegar al sistema de facturación, al de envíos y al pipeline de analítica sin que el productor sepa siquiera que existen. Por defecto, una suscripción solo recibe los mensajes publicados mientras está activa; una suscripción duradera pide al broker que guarde los mensajes para un suscriptor con nombre que está temporalmente desconectado. Sin durabilidad, un topic es una difusión; con ella, cada suscriptor tiene en la práctica una cola privada.
| Cola | Topic | |
|---|---|---|
| Consumidores por mensaje | Uno | Cero o varios |
| Consumidor desconectado | El mensaje espera | El mensaje se pierde, salvo con suscripción duradera |
| Añadir consumidores | Aumenta el throughput | Añade copias |
| Uso típico | Reparto de trabajo, comandos | Notificación de eventos, difusión |
Los brokers modernos difuminan la frontera: RabbitMQ construye ambos a partir de exchanges y bindings, y Kafka ofrece ambos mediante particiones y consumer groups. Las dos intenciones —haz esto una vez frente a avisa a todos— siguen siendo la forma correcta de pensar un diseño, sea cual sea el producto.
Lo que realmente te aporta el desacoplamiento
El «acoplamiento débil» es la justificación habitual de la mensajería, pero esconde tres beneficios distintos, cada uno con su precio.
Desacoplamiento en el tiempo. El consumidor no necesita estar en marcha cuando el productor envía. Un sistema batch nocturno puede aceptar eventos durante todo el día; un proveedor de pagos que está en mantenimiento recibe su backlog cuando vuelve. El precio es que el productor todavía no puede asegurar al usuario que algo haya ocurrido.
Desacoplamiento en el espacio. El productor se dirige a un nombre de destino, no a un host. Puedes mover un consumidor a otra máquina, ejecutar cinco copias o sustituirlo por un servicio reescrito, y la configuración del productor no cambia. El precio es que el broker se convierte en infraestructura crítica y necesita su propia estrategia de alta disponibilidad.
Desacoplamiento de la carga. Una cola absorbe picos. Un front-end web que recibe diez mil pedidos en un minuto puede encolarlos en segundos y dejar que el servicio de preparación de pedidos los procese a su ritmo, en lugar de caerse por timeout ante el pico. El precio es que la cola puede crecer sin límite si los consumidores se quedan atrás, así que todo sistema necesita un plan para el backlog: más consumidores, caducidad de mensajes o backpressure sobre el productor.
Las garantías de entrega en un párrafo
Un broker puede ofrecer entrega at-most-once (fire and forget: rápida, pero puede perder mensajes), entrega at-least-once (el broker conserva el mensaje hasta que el consumidor lo confirma, de modo que una caída antes de la confirmación provoca una reentrega y posiblemente un duplicado), o una forma de exactly-once limitada a un ámbito concreto (las transacciones de Kafka entre topics de Kafka, por ejemplo). La entrega persistente —escribir el mensaje en disco antes de confirmárselo al productor— hace que el at-least-once sobreviva a un reinicio del broker. Casi todos los sistemas en producción funcionan con at-least-once y hacen que sus consumidores sean idempotentes, es decir, que procesar el mismo mensaje dos veces tenga el mismo efecto que procesarlo una. La guía de patrones, más adelante en esta materia, explica cómo hacerlo.
Protocolos y APIs
La palabra «mensajería» abarca una API estándar, varios protocolos de red y un protocolo propio de un producto; confundirlos provoca errores de integración muy reales.
- JMS (Jakarta Messaging) es una API de Java, no un protocolo. El código escrito contra
jakarta.jmsfunciona con cualquier broker que incluya un cliente JMS, pero un cliente JMS habla con su broker en el formato de red que haya elegido el fabricante. Dos brokers no son interoperables solo porque ambos «soporten JMS». - AMQP 0-9-1 es el protocolo de red en torno al cual se construyó RabbitMQ; define exchanges, colas, bindings y channels, y cualquier lenguaje con un cliente AMQP 0-9-1 puede hablar con RabbitMQ.
- AMQP 1.0 es un estándar OASIS e ISO que apenas comparte con 0-9-1 algo más que el nombre. Define cómo se transfieren mensajes entre pares, no una topología de broker. ActiveMQ Classic, ActiveMQ Artemis, RabbitMQ (de forma nativa desde la 4.0) y Azure Service Bus lo hablan todos.
- STOMP es un protocolo de texto mínimo que puedes manejar desde una sesión de Telnet. Es popular para clientes de navegador sobre WebSocket y para lenguajes de scripting.
- MQTT es un protocolo publish-subscribe compacto, diseñado para dispositivos con recursos limitados y redes poco fiables; tanto ActiveMQ como RabbitMQ aceptan conexiones MQTT.
- El protocolo de Kafka es el protocolo binario propio de Kafka. No hay ningún estándar detrás, pero existen clientes para todos los lenguajes habituales.
- OpenWire es el protocolo binario nativo de ActiveMQ Classic, que usan sus clientes de Java, .NET y C++.
Para un equipo Java, la consecuencia práctica es esta: usa JMS cuando quieras conservar la opción de cambiar de broker, y el cliente nativo (amqp-client de RabbitMQ, kafka-clients de Kafka) cuando quieras todas las funcionalidades del broker elegido.
Cómo elegir un broker
Los tres brokers open source que cubre esta materia resuelven problemas que se solapan pero no son iguales.
| ActiveMQ Classic (y Artemis) | RabbitMQ | Apache Kafka | |
|---|---|---|---|
| Modelo | Colas y topics JMS | Los exchanges enrutan a colas | Log particionado y replicado |
| Vida de un mensaje | Se borra al confirmarse | Se borra al confirmarse | Se conserva durante un tiempo configurado, se consuma o no |
| Orden | Por cola (por grupo de mensajes con JMSXGroupID) | Por cola con un solo consumidor | Por partición |
| Reprocesar mensajes antiguos | No | No (salvo streams) | Sí, reiniciando los offsets |
| Flexibilidad de enrutamiento | Selectores, virtual topics, destinos compuestos | Muy alta (tipos de exchange, bindings, cabeceras) | Baja: el productor elige un topic y una clave |
| Throughput ideal | De miles a decenas de miles de mensajes/s | Similar, con enrutamiento rico | De cientos de miles a millones de registros/s |
| Protocolos | OpenWire, AMQP 1.0, STOMP, MQTT, WebSocket | AMQP 0-9-1, AMQP 1.0, STOMP, MQTT, streams | Protocolo de Kafka |
| Encaje natural | Aplicaciones Java EE y Spring que necesitan JMS | Colas de tareas, RPC, enrutamiento flexible, equipos políglotas | Event streaming, pipelines de logs, integración de datos, reprocesamiento |
Algunas reglas prácticas funcionan bien. Si tus consumidores son message-driven beans de JMS o métodos Spring @JmsListener y necesitas colas transaccionales, ActiveMQ es la opción con menos fricción; prefiere Artemis en despliegues nuevos que necesiten más throughput o clustering, y Classic donde ya tengas configuración y herramientas. Si necesitas enrutar un mensaje a distintas colas según varios atributos, o tienes productores en Python y consumidores en Go, el modelo de exchanges de RabbitMQ es difícil de superar. Si los datos son un flujo de eventos que varios equipos consumirán en momentos distintos, que debe poder reprocesarse o que llega en volúmenes muy altos, el log retenido de Kafka es la primitiva adecuada, y obligarlo a comportarse como una cola de tareas te decepcionará.
Dónde falla la mensajería
- Usar una cola como base de datos. Las colas son para mensajes en tránsito. Guardar estado en una cola y consultarlo más tarde lleva a un crecimiento sin límite y a un orden impredecible. Persiste el estado en un almacén; pon los eventos en la cola.
- Dar por hecho el orden entre consumidores. Una cola con tres consumidores en competencia entrega en orden pero procesa en paralelo, así que el mensaje 2 puede terminar antes que el 1. Si el orden importa, usa un consumidor por clave (grupos de mensajes, single-active-consumer de RabbitMQ, particiones de Kafka).
- Mensajes muy grandes. Un PDF de 50 MB en el cuerpo de un mensaje ralentiza cada salto y revienta los límites de memoria del broker. Guarda el fichero en un almacenamiento de objetos y envía una referencia: es el patrón claim-check.
- Ignorar el backlog. Una cola que nadie vigila se llena en silencio hasta que el broker alcanza su límite de disco y deja de aceptar mensajes de todos los productores. Configura alertas sobre la profundidad de las colas y el número de consumidores desde el primer día.
- Acoplarse a través del payload. Si todos los consumidores analizan el mismo JSON sin documentar, el sistema «desacoplado» no puede cambiar un campo sin un despliegue coordinado. Versiona el payload y añade una propiedad
schemaVersiona cada mensaje.
Ideas clave
- La mensajería es uno de los cuatro estilos de integración; cambia las respuestas inmediatas por resiliencia, elasticidad e independencia entre sistemas.
- Un mensaje son cabeceras que entiende el broker, propiedades que define la aplicación y un cuerpo opaco.
- Las colas entregan cada mensaje una vez y escalan con consumidores en competencia; los topics difunden a todos los suscriptores, de forma duradera si se pide.
- El desacoplamiento en el tiempo, en el espacio y de la carga es lo que te da un broker; backlogs sin límite y una pieza crítica de infraestructura son lo que te cuesta.
- JMS es una API de Java; AMQP, STOMP y MQTT son protocolos de red, y Kafka habla el suyo propio: no des por hecha la interoperabilidad por compartir unas siglas.
- ActiveMQ encaja en sistemas Java centrados en JMS, RabbitMQ en sistemas políglotas con mucho enrutamiento y Kafka en flujos de eventos de gran volumen que deben poder reprocesarse.
Fuentes y lecturas recomendadas
Preguntas frecuentes
- ¿Un broker de mensajes es lo mismo que un enterprise service bus?
- No. Un broker almacena y reenvía mensajes entre destinos, sin entender su contenido. Un enterprise service bus funciona sobre un broker o un transporte y añade enrutamiento basado en contenido, transformación, pasarelas entre protocolos y orquestación. Muchos productos ESB integran un broker como ActiveMQ, pero un broker puede funcionar por sí solo, y así es en la mayoría de los sistemas modernos.
- ¿Cuándo es mejor una simple llamada HTTP que un mensaje?
- Cuando quien llama necesita de verdad la respuesta para poder continuar —por ejemplo, validar una tarjeta antes de confirmar un pedido— y se espera que el servicio llamado esté disponible. La mensajería destaca cuando el trabajo puede aplazarse, cuando productores y consumidores escalan a ritmos distintos, cuando el mismo evento interesa a varios consumidores o cuando el sistema de destino a veces no está disponible.
- ¿Una cola garantiza que un mensaje se procese exactamente una vez?
- Por sí sola, no. Un broker puede prometer entrega at-most-once o at-least-once; el consumidor puede caerse después de procesar y antes de confirmar, y entonces el mismo mensaje se vuelve a entregar. El procesamiento exactly-once es una propiedad de todo el pipeline y normalmente se consigue con consumidores idempotentes o con un registro transaccional en el lado del consumidor.
- ¿Kafka es una cola de mensajes?
- Kafka es un commit log distribuido y replicado. Permite repartir trabajo como una cola mediante consumer groups y hacer pub-sub con grupos independientes, pero no borra un registro al consumirlo ni lleva confirmaciones por mensaje. Esa diferencia de modelo es la razón por la que a Kafka se le suele describir como una plataforma de event streaming y no como una cola.