La elección entre Kafka y RabbitMQ depende de qué representa el mensaje y qué debe ocurrir después de procesarlo. Una tarea suele representar trabajo pendiente que algún worker debe completar. Un evento registra algo que ocurrió y puede interesar a varios servicios, incluso a uno que empiece a consumir más tarde.

El enrutamiento, la retención y la recuperación concretan esa diferencia. La comparación más habitual enfrenta las colas clásicas o quorum de RabbitMQ con el log particionado de Kafka; RabbitMQ también ofrece un modelo de Streams con retención y replay.

RabbitMQ y las colas de tareas

RabbitMQ recibe publicaciones en un exchange y las enruta hacia colas según sus reglas y bindings. Esa separación permite dirigir trabajo por claves, patrones o destinos, y distribuir copias hacia varias colas cuando corresponde.

Un conjunto de workers puede competir por las entregas de una misma cola. Con confirmación manual, el consumidor envía un acknowledgement después de completar el procesamiento. El broker puede entonces retirar el mensaje de esa cola; las copias en otras colas tienen su propio ciclo de entrega y confirmación.

Si un consumidor pierde la conexión con mensajes sin confirmar, esos mensajes pueden volver a entregarse. El rechazo, la reencolación, los plazos de expiración y el envío a una cola de mensajes fallidos modifican ese comportamiento. La política de recuperación debe distinguir un error temporal de una tarea que no puede completarse.

Este modelo encaja con envío de correos, generación de reportes y otros trabajos en segundo plano. El enrutamiento flexible permite separar responsabilidades y aplicar políticas por cola. Un flujo de pagos también puede usarlo, siempre que los efectos se coordinen de forma idempotente.

Para una topología de tareas y workers, el modelo puede resultar fácil de seguir. La simplicidad operativa y la latencia dependen, sin embargo, del tipo de cola, la replicación, el backlog y el despliegue. Una cola con crecimiento continuo o un ciclo de reintentos sin límites sigue necesitando control de capacidad.

Kafka y el log de eventos

Kafka organiza los registros en topics divididos en particiones. Los productores añaden registros al log y cada partición conserva un orden por offset. Una clave permite dirigir eventos relacionados a una partición, bajo la estrategia de particionado utilizada.

En un grupo de consumo convencional, las particiones se reparten entre consumidores. El grupo registra sus offsets para saber hasta dónde avanzó. Grupos distintos conservan su progreso de forma independiente, por lo que analítica, notificaciones y proyecciones pueden leer el mismo flujo sin competir por una única entrega.

Leer o confirmar un offset no elimina el registro. La retención se configura por tiempo o tamaño, y algunos topics utilizan compactación por clave. El replay solo alcanza los datos que todavía existen. Un log compactado conserva información según su política, pero no necesariamente cada evento histórico de una entidad.

Kafka encaja cuando varios servicios necesitan un flujo retenido, reconstruir proyecciones o incorporar nuevos consumidores sin depender del progreso de los anteriores. El historial debe dimensionarse según la recuperación que se pretende soportar; Kafka no funciona automáticamente como un archivo permanente.

El orden es por partición. Los grupos, los rebalanceos y el procesamiento concurrente requieren atención adicional: el orden de lectura no garantiza por sí solo el orden de finalización de los efectos. En RabbitMQ, varios consumidores, prioridades o nuevas entregas también pueden cambiar el orden observado al procesar una cola.

Dos modelos de consumo

El diagrama muestra una cola atendida por workers y un topic leído por dos grupos independientes. Una publicación de RabbitMQ puede tener otras rutas y colas; el ejemplo representa un destino de tareas.

---
config:
  look: handDrawn
---
flowchart LR
    subgraph rabbit["RabbitMQ · Cola de tareas"]
        direction TB
        producerR["Productor"] --> exchange["Exchange<br/>Reglas de enrutamiento"]
        exchange --> queue[("Cola")]
        queue --> workers["Workers competidores<br/>Procesar y confirmar"]
        workers -.->|"Ack de entrega"| queue
    end
    subgraph kafka["Kafka · Log de eventos"]
        direction TB
        producerK["Productor"] --> topic[("Topic particionado<br/>Retención configurada")]
        topic --> groupA["Grupo A<br/>Offsets propios"]
        topic --> groupB["Grupo B<br/>Offsets propios"]
    end
    rabbit ~~~ kafka
AspectoColas de RabbitMQLog de Kafka
DistribuciónExchanges enrutan hacia una o varias colas.Productores publican en topics particionados.
ConsumoWorkers compiten por entregas de una cola.Grupos independientes leen el mismo flujo.
ProgresoEl ack confirma una entrega en su cola.El commit registra el offset del grupo.
HistorialLos mensajes confirmados dejan de estar pendientes en esa cola.Los registros permanecen según la retención.
RecuperaciónNueva entrega de trabajo pendiente.Reanudar offsets o releer datos retenidos.

La nueva entrega de una tarea pendiente y el replay de un historial sirven a necesidades distintas. Si se requiere un log dentro de RabbitMQ, el modelo de Streams debe evaluarse con su propio contrato de retención y consumo.

Entrega e idempotencia

Tanto RabbitMQ como Kafka permiten configurar flujos de entrega al menos una vez. En ese modelo, un registro puede procesarse más de una vez durante la recuperación. La garantía depende de las confirmaciones, los reintentos, la persistencia, la retención y las condiciones de fallo contempladas; no constituye una promesa incondicional de ausencia de pérdidas.

En RabbitMQ, los publisher confirms informan al productor de que el broker asumió responsabilidad por la publicación según la cola y su configuración. Son independientes del ack del consumidor y no demuestran que el trabajo terminó. Una publicación sin ruta puede recibir confirmación; la detección y el tratamiento de mensajes sin destino requieren la política correspondiente, por ejemplo publicación con mandatory y manejo de las devoluciones.

La durabilidad también depende de la configuración de colas y mensajes y de su replicación. Si un worker completa el efecto y cae antes de enviar el ack, la entrega puede repetirse. Confirmar antes de completar el efecto puede dejar trabajo sin realizar después de un fallo.

En Kafka, procesar y después confirmar el offset deja una ventana similar: si el proceso cae entre ambas acciones, el registro puede volver a procesarse. Confirmar el offset antes de completar el trabajo puede hacer que el grupo avance aunque el efecto no se haya realizado.

La confirmación al productor tiene otro alcance. Con acks=all, la escritura espera la confirmación de las réplicas que forman el conjunto sincronizado, o ISR, en ese momento. min.insync.replicas establece el umbral mínimo para admitir esa escritura. La replicación, la elección de líder y las políticas de recuperación completan el contrato de durabilidad y disponibilidad.

Por ello, los consumidores necesitan una identidad estable del evento u operación y una forma de reconocer los efectos ya realizados. Si el efecto está en una base de datos, la deduplicación y el cambio local pueden guardarse en una misma transacción. Un cobro externo exige además coordinación e idempotencia bajo las garantías del proveedor.

Kafka ofrece mecanismos de exactamente una vez dentro de un alcance definido. La idempotencia del productor evita duplicados de ciertos reenvíos del protocolo; no identifica automáticamente dos solicitudes de negocio equivalentes. Las transacciones pueden coordinar registros producidos en Kafka y offsets consumidos, con lectores configurados para ver datos confirmados mediante read_committed. Ese mecanismo no hace atómico un cobro o una escritura en una base externa junto con el offset.

Rendimiento y operación

Kafka aprovecha batching, escrituras secuenciales, compresión y particiones para distribuir carga. RabbitMQ mantiene el enrutamiento y el estado de las entregas de sus colas. Ambos consumen recursos para almacenar, transmitir y replicar datos; el coste depende del modelo y la configuración.

Una comparación de 4,000 a 10,000 mensajes por segundo en RabbitMQ con más de un millón de mensajes por segundo en Kafka necesita describir la carga y las condiciones de medición. Esas cifras, por sí solas, no establecen tasas típicas, límites de producto ni una ventaja constante de 100 veces.

Un benchmark útil debe especificar:

  • Tamaño de los mensajes, batching y compresión.
  • Persistencia, réplicas y política de confirmación.
  • Número de productores, consumidores, colas o particiones.
  • Hardware, red y distribución de los destinos o claves.
  • Latencia objetivo, backlog tolerado y comportamiento durante fallos.

La capacidad relevante incluye completar los efectos del consumidor y recuperarse bajo carga. Publicar rápido mientras crece el trabajo pendiente no demuestra que el flujo completo tenga esa capacidad.

Cómo elegir

RabbitMQ suele encajar cuando se necesita asignar tareas, enrutar mensajes hacia destinos concretos y controlar su ciclo de entrega. Kafka suele encajar cuando se necesita conservar un flujo, releerlo y permitir que varios grupos mantengan progreso independiente. La operación y la latencia deben validarse para la carga real en ambos casos.

Ambos pueden convivir si hay necesidades distintas: un evento retenido en Kafka puede originar una tarea dirigida a RabbitMQ. El puente añade otro punto de fallo y otra entrega que puede repetirse. La identidad del evento debe conservarse y los consumidores deben tolerar que la tarea se publique de nuevo.

Cuando una operación también modifica una base de datos, la publicación requiere resolver la escritura dual. La outbox transaccional descrita en SD: 2PC vs Saga conserva el cambio local y el evento pendiente en una misma transacción; el relay y los consumidores mantienen sus propias reglas de recuperación e idempotencia.

Qué verificar

Las pruebas centrales detienen un consumidor después de producir el efecto y antes de confirmar, pierden una confirmación al productor y repiten publicaciones. También deben cubrir mensajes sin ruta, fallos de nodos, mensajes que no pueden procesarse y retrasos prolongados de los consumidores.

Para Kafka, conviene añadir rebalanceos, consumidores concurrentes y recuperación después de que venza la retención. Para RabbitMQ, deben comprobarse la reencolación, los límites de mensajes sin confirmar y el destino de las tareas fallidas.

Las señales útiles incluyen profundidad y antigüedad de las colas, mensajes sin confirmar, lag por grupo y partición, latencia de extremo a extremo, reintentos y efectos duplicados. El contrato queda comprobado cuando puede explicarse qué trabajo sigue pendiente, qué historial permanece disponible y cómo se recupera cada efecto incierto.