Event Streaming

Apache Kafka

Kafka es un commit log distribuido de solo anexado (append-only), no una cola. Los registros se escriben una vez y se leen muchas veces, lo que le otorga capacidades de replay, fan-out y garantías de orden que un broker clásico no puede igualar.

advanced15 min readUpdated 16 sept 2026
producer.ts
ts
// producer.ts
import { Kafka } from "kafkajs";

const kafka = new Kafka({
  clientId: "orders",
  brokers: ["localhost:9092"],
});

const producer = kafka.producer();

await producer.connect();
await producer.send({
  topic: "orders.created",
  messages: [
    // The key decides the partition, so all events for one
    // customer stay in order.
    { key: order.customerId, value: JSON.stringify(order) },
  ],
});
await producer.disconnect();
Lanzamiento
2011
Origen
LinkedIn
Modelo
Append-only commit log
Orden
Por partición
Entrega predeterminada
At-least-once
Retención
Basada en tiempo o tamaño

Por que importa

Por qué los equipos eligen Kafka

Un log duradero y reproducible

Los registros se anexan y nunca se eliminan al leerlos. Cualquier consumer puede retroceder y reprocesar el historial, algo que una cola clásica simplemente no puede ofrecer.

Particiones para escala y orden

Un topic se divide en particiones que se distribuyen entre los brokers. El paralelismo proviene de las particiones y el orden está garantizado dentro de cada una de ellas.

Múltiples lectores independientes

Los consumer groups rastrean sus propios offsets, por lo que un proceso de facturación, un indexador de búsqueda y un pipeline de analíticas pueden leer los mismos eventos sin interferir entre sí.

La imagen completa

Las tres ideas detrás de Kafka

Un topic es un log de solo anexado, los producers escriben registros con claves en él y los consumers rastrean su propia posición mediante offsets.

Topic

Anexar

Un log particionado con nombre. Los producers anexan registros al final y cada registro mantiene su offset hasta que expira la retención.

Consumer group

Compartir

Un grupo de consumers divide las particiones entre ellos y rastrea sus propios offsets confirmados, manteniendo la escala y el progreso de forma independiente.

Offset

Rastrear

La posición de un grupo en una partición. Confirmarlo es el acuse de recibo que decide entre at-least-once y at-most-once.

HTML5 de un vistazo

Componentes básicos de Kafka

Topics

Logs con nombre de solo anexado que mantienen los registros hasta que expira la retención.

Particiones

La unidad de paralelismo y el límite del ordenamiento.

Producers

Serializan un registro, eligen una clave y lo anexan a una partición.

Consumer groups

Dividen las particiones entre sus miembros y confirman los offsets.

Offsets

La posición de un grupo en una partición y su punto de confirmación.

Retención

Eliminación por tiempo o tamaño, o compactación para mantener el último valor por clave.

Flujo

El viaje de un registro

Un registro se escribe una vez y se lee muchas veces. Nada se elimina cuando un consumer lo lee, que es precisamente lo que hace posible el replay.

  1. 1

    Producir

    El producer serializa un registro y elige una partición aplicando un hash a su clave, para que la misma clave siempre caiga en el mismo lugar.

  2. 2

    Anexar

    El broker líder de la partición anexa el registro al final de su log y devuelve el nuevo offset al producer.

  3. 3

    Consumir

    Cada consumer de un grupo posee un subconjunto de las particiones y lee sus registros en orden de offset.

  4. 4

    Confirmar el offset

    Tras el procesamiento, el consumer confirma su posición para que el grupo pueda reanudar desde allí después de un reinicio.

  5. 5

    Retener o reproducir

    El registro permanece en el log hasta que expira la retención, permitiendo que otro grupo o un proceso posterior retroceda y lo lea de nuevo.

Una breve historia

De los logs de LinkedIn al estándar de streaming

  1. 2011

    Kafka se vuelve open-source

    LinkedIn lanza Kafka como un commit log distribuido para sus flujos de actividad.

    11
  2. 2012

    Incubación en Apache

    Kafka se convierte en un proyecto de Apache y su adopción se extiende mucho más allá de LinkedIn.

    12
  3. 2016

    Kafka Streams

    La versión 0.10 incluye una librería de procesamiento de streams, y ksqlDB llega más tarde.

    16
  4. 2017

    Semántica Exactly-once

    La versión 0.11 añade producers idempotentes y transacciones para pipelines exactly-once.

    17
  5. 2021

    Comienza KRaft

    El KIP-500 inicia la sustitución de ZooKeeper por un quórum de metadatos integrado en los brokers.

    21
  6. 2025

    Kafka 4.0 elimina ZooKeeper

    Los nuevos clusters funcionan solo en modo KRaft y termina la era de ZooKeeper.

    25

La guia completa

Apache Kafka: Todo lo que necesitas saber

Qué es Kafka en realidad

Apache Kafka es un commit log distribuido y de solo anexado (append-only). Esa única frase lo explica casi todo. Los registros se añaden al final de un log, cada uno recibe un número incremental llamado offset, y nada se muta nunca en su lugar. Los lectores no eliminan los registros cuando los consumen; simplemente desplazan un cursor hacia adelante.

Esta es la diferencia fundamental con una cola de mensajes clásica. En una cola, un consumidor toma un mensaje y este desaparece. En Kafka, un consumidor lee un registro, recuerda su offset y el registro permanece donde está mientras la política de retención del topic lo permita. Diez consumidores diferentes —y diez aplicaciones distintas— pueden leer el mismo registro de forma independiente, a su propio ritmo y sin coordinarse entre sí.

Kafka fue creado en LinkedIn para gestionar flujos de actividad: vistas de página, clics, líneas de log, todo a millones de eventos por segundo. Se liberó como código abierto en 2011 y se convirtió en un proyecto de Apache en 2012. Hoy en día es la infraestructura estándar para el streaming de eventos: change data capture, pipelines de métricas, event sourcing, agregación de logs y procesamiento de streams.

Si vienes de RabbitMQ o Redis, el cambio de mentalidad consiste en dejar de pensar en “un mensaje que debe entregarse” y empezar a pensar en “un hecho que ha sido registrado”.

Topics, particiones y ordenamiento

Un topic es un log duradero con nombre. Los producers escriben en él y los consumers leen de él. Los topics se dividen en partitions, y la partición es la unidad tanto de paralelismo como de ordenamiento.

  • Los registros dentro de una misma partición están estrictamente ordenados por offset.
  • No hay garantía de orden entre particiones.
  • Tener más particiones permite más consumers en paralelo, a costa de generar más archivos, más replicación y rebalances más lentos.

Esta es la garantía más importante en Kafka: el orden es por partición. Si dos eventos deben procesarse en orden, deben caer en la misma partición. De lo contrario, Kafka podría entregarlos a diferentes consumers que se ejecuten simultáneamente y el orden se perdería.

Las particiones también determinan el paralelismo máximo de consumers en un grupo: un grupo puede tener como máximo un consumer por partición leyéndola activamente. Seis particiones significan como máximo seis consumers útiles; un séptimo quedaría inactivo.

# three partitions, replicated across three brokers
kafka-topics.sh --create \
  --topic orders.created \
  --partitions 6 \
  --replication-factor 3 \
  --bootstrap-server localhost:9092

Es posible añadir particiones más tarde, pero esto cambia el mapeo de clave a partición para las claves existentes, por lo que los eventos de una misma entidad podrían terminar divididos en dos particiones y perder su orden relativo. Decide el número de particiones al crear el topic, dejando margen para el crecimiento.

Productores, claves y particionamiento

Un productor serializa un registro y decide a qué partición pertenece. El particionador por defecto aplica un hash a la clave (key) del registro y la mapea a una partición. La misma clave siempre se dirige a la misma partición, que es la forma de preservar el orden para una entidad mientras se distribuyen diferentes entidades entre las particiones.

await producer.send({
  topic: "orders.created",
  messages: [
    { key: order.customerId, value: JSON.stringify(order) },
  ],
});

Elegir la clave es una decisión de diseño, no un detalle. Si usas como clave customerId, todos los eventos de un cliente estarán ordenados entre sí. Si usas orderId, obtendrás la máxima distribución pero sin orden entre eventos. Los registros con una clave nula se distribuyen para mantener el equilibrio —el sticky partitioner de Kafka llena una partición antes de pasar a la siguiente— pero no ofrecen ninguna garantía de orden.

El productor también controla la durabilidad y el rendimiento (throughput) mediante el procesamiento por lotes (batching) y los acuses de recibo (acknowledgements). acks decide cuántas réplicas deben confirmar una escritura, linger.ms y batch.size deciden cuánto tiempo se espera para llenar un lote, y compression.type decide cuánto CPU se intercambia por red y disco. Más detalles sobre esto a continuación.

Brokers, replicación y el líder

Un broker es un servidor de Kafka. Un clúster es un conjunto de varios brokers trabajando juntos. Cada partición tiene un broker leader y cero o más followers. Los productores y consumidores se comunican con el líder; los followers replican el log.

La replicación es lo que hace que Kafka sea duradero. Si un líder falla, una de las réplicas sincronizadas (ISR) es promovida y el clúster sigue operando. El ajuste acks decide cuánto tiempo espera el productor:

  • acks=0 — enviar y olvidar; es la opción más rápida y la menos segura.
  • acks=1 — el líder lo escribió; se pierde si el líder falla antes de la replicación.
  • acks=all — todas las réplicas sincronizadas lo confirmaron; es la opción más segura.

Combina acks=all con min.insync.replicas=2 y un factor de replicación de tres para un entorno de producción: una escritura solo se confirma cuando al menos dos réplicas la tienen, por lo que perder un broker no implica pérdida de datos.

const producer = kafka.producer({
  idempotent: true,
  maxInFlightRequests: 1,
  transactionalId: "orders-producer",
});

El productor idempotente añade un número de secuencia a cada lote para que el broker pueda descartar duplicados, lo que elimina los duplicados accidentales que los reintentos podrían introducir. Un clúster también tiene un controller que gestiona el liderazgo de las particiones y los metadatos. Las versiones antiguas de Kafka utilizaban ZooKeeper para esto; el Kafka moderno utiliza KRaft, donde los brokers forman su propio quórum de metadatos y ZooKeeper desaparece por completo.

Grupos de consumidores y asignación de particiones

Los consumidores pertenecen a un consumer group, identificado por groupId. Kafka asigna cada partición a exactamente un consumidor dentro del grupo. Esto te proporciona dos ventajas simultáneamente: escalado horizontal, ya que las particiones se comparten, y balanceo de carga, porque no hay dos consumidores en un grupo que procesen la misma partición.

const consumer = kafka.consumer({ groupId: "billing" });

await consumer.connect();
await consumer.subscribe({ topic: "orders.created", fromBeginning: true });

await consumer.run({
  eachMessage: async ({ partition, message }) => {
    const order = JSON.parse(message.value!.toString());
    console.log(`p${partition} @ ${message.offset}`, order.id);
  },
});

Cuando los consumidores se unen o abandonan el grupo, Kafka realiza un rebalance: revoca las asignaciones y entrega unas nuevas. Los rebalances pausan el consumo, por lo que son costosos; los protocolos de rebalanceo cooperativo reducen la interrupción moviendo únicamente las particiones que necesitan ser trasladadas. Un consumidor que deje de enviar heartbeats durante session.timeout.ms se considera muerto y sus particiones son reasignadas.

El grupo es también la forma en que Kafka recuerda el progreso. Cada grupo tiene sus propios offsets confirmados almacenados en el topic interno __consumer_offsets, por lo que dos grupos que lean el mismo topic pueden estar en posiciones completamente diferentes sin necesidad de coordinación.

Rebalanceo y el bucle de poll

Bajo el capó, un consumidor es un bucle de poll. Recupera registros, los entrega a tu handler y luego vuelve a recuperar más. El broker rastrea la disponibilidad por separado mediante heartbeats, por lo que un handler lento no parece estar muerto inmediatamente, pero existe un límite estricto: si un solo poll tarda más de max.poll.interval.ms, el broker asume que el consumidor está bloqueado y dispara un rebalanceo.

const consumer = kafka.consumer({
  groupId: "billing",
  sessionTimeout: 45_000,
  heartbeatInterval: 3_000,
  rebalanceTimeout: 60_000,
});

Un rebalanceo se dispara cada vez que un consumidor se une, sale o es expulsado, cuando cambian las particiones o los topics, y cuando cambia una suscripción. Durante el rebalanceo, Kafka revoca las asignaciones y pausa el consumo, por lo que los rebalanceos frecuentes afectan gravemente el throughput. Tres hábitos ayudan a que sean poco comunes:

  • Limita el trabajo por registro. Un handler que a veces se ejecute durante minutos eventualmente superará max.poll.interval.ms.
  • Usa rebalanceo cooperativo. El protocolo cooperativo incremental mueve solo las particiones que deben moverse, en lugar de detener a todo el grupo.
  • Usa membresía estática. Configurar un group.instance.id estable permite que un consumidor reiniciado reclame sus particiones anteriores sin disparar un rebalanceo.

Kafka también te ofrece consumer.pause() y consumer.resume() para aplicar backpressure cuando una dependencia downstream tiene problemas. Pausar es mejor que bloquear el bucle de poll: el consumidor sigue enviando heartbeats, permanece en el grupo y simplemente deja de recuperar datos hasta que estés listo.

Offsets y garantías de entrega

Un offset es la posición de un grupo de consumidores en una partición. Cuando un consumidor procesa un registro, puede hacer un commit del offset, lo que le indica a Kafka: “este grupo ha terminado todo hasta este punto”. Si haces el commit demasiado pronto y ocurre un fallo, se pierde trabajo; si lo haces demasiado tarde y ocurre un fallo, el trabajo se procesa de nuevo.

  • At-most-once (como máximo una vez) — commit antes del procesamiento. Si hay un fallo, se pierde el registro.
  • At-least-once (al menos una vez) — procesar y luego hacer el commit. Si hay un fallo, el registro se procesa de nuevo. Este es el valor predeterminado más común.
  • Exactly-once (exactamente una vez) — las transacciones y los productores idempotentes hacen que el ciclo de lectura-procesamiento-escritura sea atómico dentro de Kafka.

El auto-commit ocurre mediante un temporizador en segundo plano. Es conveniente pero peligroso, ya que puede hacer commit de offsets de registros que aún se están procesando. El commit manual después de finalizar el trabajo es la opción predeterminada más fiable.

await consumer.run({
  autoCommit: false,
  eachMessage: async ({ topic, partition, message }) => {
    await chargeOrder(JSON.parse(message.value!.toString()));
    await consumer.commitOffsets([
      { topic, partition, offset: String(Number(message.offset) + 1) },
    ]);
  },
});

Dado que at-least-once es la norma, los handlers deben ser idempotentes. La guía de Batch Processing cubre el mismo contrato para las colas de trabajos: asume que el trabajo puede ejecutarse dos veces y haz que la segunda ejecución no realice ninguna acción (no-op). Una clave de deduplicación derivada del trabajo —y no del offset de Kafka— hace que el handler sea seguro incluso si el mismo evento lógico se produce dos veces.

Exactly-once es real, pero más limitado de lo que parece. Funciona para pipelines de Kafka a Kafka con productores transaccionales y isolation.level=read_committed, pero en el momento en que escribes en una base de datos externa, vuelves a necesitar escrituras idempotentes en dicha base de datos.

Retención, replay y compactación de logs

Kafka conserva los registros basándose en una retention policy, no en un acuse de recibo de entrega. retention.ms (por defecto siete días) y retention.bytes limitan cada partición; cuando se excede cualquiera de los dos, los segmentos de log antiguos se eliminan. Debido a que nada se elimina al leer, un consumidor puede hacer un replay del historial buscando un offset anterior o reiniciando el grupo.

# rewind a group to the beginning of a topic
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group billing --topic orders.created \
  --reset-offsets --to-earliest --execute

El replay es el superpoder de Kafka. Un nuevo servicio puede iniciarse leyendo todo el historial de un topic. Se puede desplegar la corrección de un bug y reprocesar el último día. Un pipeline de analíticas puede reconstruirse desde el log de eventos original. Una cola no puede hacer nada de esto, porque los datos ya habrían desaparecido.

Para el keyed state, el otro modo de retención es la log compaction. Con cleanup.policy=compact, Kafka conserva al menos el último valor para cada clave y descarta los valores más antiguos. Un valor null es un tombstone que elimina la clave. El log se convierte en un changelog que puede reconstruir una tabla — exactamente lo que Kafka Streams utiliza para sus state stores y en lo que se basan los pipelines de change-data-capture.

# keep the latest value per key instead of deleting by age
kafka-configs.sh --bootstrap-server localhost:9092 \
  --alter --entity-type topics --entity-name user.profiles \
  --add-config cleanup.policy=compact,min.cleanable.dirty.ratio=0.1

Esquemas y el Schema Registry

Debido a que el log sobrevive a cualquier aplicación individual, la estructura de un registro se convierte en un contrato entre equipos. Un productor y un consumidor están acoplados por los bytes que intercambian, y ese acoplamiento persiste a través de los despliegues. Un schema registry transforma esto en un problema de compatibilidad gestionado en lugar de una sorpresa.

El registry almacena esquemas versionados —generalmente Avro, Protobuf o JSON Schema— y asigna a cada uno un id numérico. Los productores registran un esquema y escriben el id junto al payload; los consumidores recuperan el esquema mediante el id y realizan la deserialización. Cuando un esquema cambia, el registry impone un modo de compatibilidad, como backward o forward, rechazando cualquier cambio que pueda romper la lectura de los consumidores existentes.

import { SchemaRegistry } from "@kafkajs/confluent-schema-registry";

const registry = new SchemaRegistry({ host: "http://localhost:8081" });

const encoded = await registry.encode(schemaId, {
  orderId: order.id,
  totalCents: order.totalCents,
});

await producer.send({
  topic: "orders.created",
  messages: [{ key: order.customerId, value: encoded }],
});

Incluso si no utilizas un registry, trata los payloads como una API: añade campos en lugar de renombrarlos, asigna un valor por defecto a los campos nuevos y versiona cuando el significado cambie. Un consumidor que ejecute la versión anterior debe ser capaz de leer un registro escrito por la versión siguiente.

Kafka Connect y Kafka Streams

Dos partes de la plataforma te evitan tener que escribir la misma infraestructura repetidamente.

Kafka Connect es un framework para mover datos hacia y desde Kafka mediante configuración en lugar de código. Los conectores de origen (source connectors) extraen datos de bases de datos, almacenamiento de objetos o APIs de SaaS; los conectores de destino (sink connectors) envían datos a warehouses, índices de búsqueda u otra base de datos. Debezium, por ejemplo, convierte un write-ahead log de PostgreSQL en un flujo de eventos de cambio. Connect se ejecuta como un clúster, rastrea los offsets y reintenta los fallos, por lo que es la respuesta estándar para “meter datos en Kafka” y “sacar datos de Kafka”.

Kafka Streams es una librería de cliente para procesar datos en Kafka. Te proporciona un DSL de streams — map, filter, groupByKey, join, window — sobre abstracciones de KStream y KTable, con almacenes de estado (state stores) respaldados por topics compactados. Se ejecuta dentro de tu aplicación, escala añadiendo instancias y gestiona la tolerancia a fallos mediante topics de changelog. Si prefieres SQL, ksqlDB ofrece una capa de consultas basada en los mismos conceptos.

const stream = builder.stream("orders.created");

stream
  .filter((key, order) => order.totalCents > 10_000)
  .groupBy((key) => order.customerId)
  .windowedBy(tumblingWindow({ size: 60 * 60 * 1000 }))
  .count()
  .toStream()
  .to("customer.hourly_orders");

Ambos valen la pena conocerlos incluso si empiezas con productores y consumidores simples, ya que definen cómo es “la manera de Kafka” (the Kafka way) a escala.

Un ejemplo minimalista de extremo a extremo

Es útil ver todo el conjunto en un solo archivo: conectar un productor y un consumidor, suscribirse, ejecutar y cerrar la aplicación correctamente.

import { Kafka } from "kafkajs";

const kafka = new Kafka({
  clientId: "orders-app",
  brokers: ["localhost:9092"],
});

const producer = kafka.producer();
const consumer = kafka.consumer({ groupId: "orders-app" });

async function main() {
  await producer.connect();
  await producer.send({
    topic: "orders.created",
    messages: [{ key: "customer_1", value: JSON.stringify({ id: "order_1" }) }],
  });

  await consumer.connect();
  await consumer.subscribe({ topic: "orders.created", fromBeginning: true });
  await consumer.run({
    eachMessage: async ({ partition, message }) => {
      const order = JSON.parse(message.value!.toString());
      console.log(`p${partition} @ ${message.offset}`, order.id);
    },
  });
}

async function shutdown() {
  await consumer.disconnect();
  await producer.disconnect();
  process.exit(0);
}

process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

En un entorno de producción, hay tres aspectos fundamentales en ese archivo. El productor se conecta una sola vez y se reutiliza, en lugar de crearse por cada mensaje. El consumidor se suscribe antes de ejecutarse. Y el proceso gestiona SIGTERM, ya que un cierre forzado durante un rebalanceo o un commit deja al grupo en un estado peor que un cierre controlado.

Event sourcing y el outbox pattern

A menudo se describe a Kafka como la columna vertebral del event sourcing, donde el log es la fuente de verdad y el estado actual es una proyección de eventos. En lugar de almacenar únicamente la fila más reciente, almacenas la secuencia de hechos — order.created, order.paid, order.shipped — y reconstruyes cualquier vista reproduciéndolos. Los compacted topics convierten la proyección en una tabla; la retención la hace auditable.

La parte difícil nunca es escribir el evento; es escribir el evento y la fila de la base de datos de forma atómica. Un proceso puede fallar después de hacer el commit de la fila y antes de publicar, o después de publicar y antes de hacer el commit. Publicar dentro de una transacción de base de datos es imposible, por lo que la respuesta estándar es el outbox pattern: escribir el evento en una tabla outbox en la misma transacción que el cambio de estado, y luego dejar que un relay o un conector CDC publique esas filas en Kafka.

BEGIN;
INSERT INTO orders (id, status, total_cents)
VALUES ($1, 'created', $2);

INSERT INTO outbox (id, topic, payload)
VALUES ($1, 'orders.created', $2);
COMMIT;

Debezium o un pequeño relay de polling luego rastrea el outbox y produce hacia el topic, eliminando las filas una vez que han sido publicadas. Debido a que la escritura en el outbox comparte la transacción, el evento existe exactamente al mismo tiempo que el estado. Los consumidores deben seguir siendo idempotentes, ya que el relay puede publicar una fila dos veces después de un fallo.

Desarrollo y pruebas locales

No necesitas un cluster de Kafka completo para desarrollar. Un broker de un solo nodo en Docker Compose, o Redpanda en modo de compatibilidad, te proporciona topics, consumer groups y offsets directamente en tu laptop.

services:
  kafka:
    image: redpandadata/redpanda:latest
    command: >
      redpanda start --overprovisioned --smp 1
      --kafka-addr PLAINTEXT://0.0.0.0:9092
      --advertise-kafka-addr PLAINTEXT://localhost:9092
    ports:
      - "9092:9092"

Para las pruebas, el patrón es el mismo que con cualquier otra dependencia de integración: inicia el broker en un contenedor, crea los topics que necesite la prueba y utiliza un group id único por cada ejecución de prueba para que los offsets confirmados nunca se filtren entre ejecuciones. Reinicia los offsets explícitamente cuando una prueba necesite leer el historial, y realiza las aserciones sobre los registros que recibió tu consumer en lugar de basarte en el tiempo.

const groupId = `test-${crypto.randomUUID()}`;
const consumer = kafka.consumer({ groupId });

Mantén los handlers del consumer puros y ligeros —parsear, validar, llamar a un servicio— para que la mayor parte de la lógica pueda probarse mediante unit tests sin necesidad de un broker. Reserva las pruebas de integración para el cableado: ¿llega un registro producido al handler correcto? y ¿se confirma el offset después?

Throughput, latencia y batching

Kafka es rápido porque utiliza batching y porque escribe de forma secuencial. Ambos aspectos son ajustables, y dicha configuración funciona como un dial entre latencia y throughput.

  • linger.ms — cuánto tiempo espera un producer para acumular un batch. Un valor más alto implica batches más grandes y mayor throughput, a costa de añadir latencia.
  • batch.size — el máximo de bytes por batch de partición.
  • compression.typesnappy, lz4 o zstd. La compresión reduce el uso de red y disco; zstd suele ganar en cuanto a ratio.
  • fetch.min.bytes y fetch.max.wait.ms — cuánto tiempo esperan los consumers para llenar una respuesta de fetch.
  • max.poll.records — cuántos records procesa un consumer por poll. Auméntalo para vaciar los backlogs más rápido, pero mantén el procesamiento por debajo de max.poll.interval.ms o el consumer será expulsado del grupo.
const producer = kafka.producer({
  linger: { ms: 20 },
  compression: CompressionTypes.GZIP,
});

const consumer = kafka.consumer({
  groupId: "billing",
  maxBytesPerPartition: 1_048_576,
  maxWaitTimeInMs: 500,
});

Un producer optimizado para throughput podría usar linger.ms=20, un batch de un megabyte y zstd. Un producer que deba publicar en milisegundos de un solo dígito utiliza linger.ms=0. No hay una única respuesta correcta; solo existe el trade-off que elijas y el p99 que puedas tolerar.

Cómo elegir entre Kafka, RabbitMQ y Redis

A menudo se comparan estos tres como si fueran intercambiables. No lo son.

  • Kafka es un log reproducible. Elígelo cuando necesites un alto rendimiento (throughput), historial duradero, fan-out hacia muchos consumidores independientes, event sourcing, procesamiento de streams o la capacidad de reprocesar datos. Es el más complejo de operar.
  • RabbitMQ es un message broker. Elígelo cuando el enrutamiento sea fundamental: exchanges, routing keys, confirmaciones (acknowledgements) por mensaje, prioridades y dead-lettering. Es ideal para la distribución de tareas y el enrutamiento complejo, y es más ligero que Kafka en volúmenes moderados. Consulta la guía de RabbitMQ.
  • Redis es un almacén de estructuras de datos en memoria que también funciona como cola de trabajos. Elígelo cuando la tarea sea sencilla, el volumen sea moderado y ya estés utilizando Redis. BullMQ sobre Redis te ofrece reintentos, programación de tareas y una UI con casi nula carga operativa. Consulta Redis Queues.

Una regla útil: si los consumidores necesitan reproducir eventos, hacer fan-out hacia muchos grupos o leer el historial, usa Kafka. Si un mensaje debe ser enrutado a un worker específico y luego olvidado, usa RabbitMQ. Si se trata de un trabajo en segundo plano con una política de reintentos, usa Redis.

Seguridad y control de acceso

Kafka suele contener los datos más valiosos de la infraestructura, así que asegúralo bien.

  • Cifrado — habilita TLS para el tráfico entre brokers y entre cliente y broker. Kafka en texto plano dentro de una VPC sigue siendo texto plano.
  • Autenticación — SASL/SCRAM o mTLS para los clientes. Evita los listeners sin autenticación en cualquier entorno que no sea una configuración local desechable.
  • Autorización — utiliza ACLs para conceder permisos de lectura, escritura o creación en topics y grupos específicos. Un servicio no debería poder leer todos los topics solo por el hecho de poder conectarse.
  • Cuotas — limita el ancho de banda de productores y consumidores por cliente para que un servicio descontrolado no pueda saturar el cluster.
  • Secretos — nunca incluyas credenciales directamente en un productor; inyéctalas desde el entorno o mediante un gestor de secretos.

Monitoreo y consumer lag

Kafka falla en silencio si lo permites. Hay cuatro señales que cubren la mayor parte de la realidad operativa.

  • Consumer lag — la brecha entre el offset más reciente y el offset committeado de un grupo, por partición. Un lag creciente es la primera señal de que los consumidores no pueden mantener el ritmo.
  • Under-replicated partitions — un conteo distinto de cero significa que un broker está caído o lento y la durabilidad está en riesgo.
  • Latencia de red y de solicitudes — los percentiles del lado del broker revelan cuándo los discos o la red son el cuello de botella.
  • Uso de disco y conteo de segmentos — Kafka depende del disco, y la replicación multiplica cada byte por el factor de replicación.

Muestra el lag en el mismo dashboard que tus servicios y configura alertas basadas en la tendencia, no en un pico aislado. Un lag que crece constantemente durante el día es un problema de capacidad; un lag que tiene un pico y luego se recupera suele ser un rebalance o un despliegue lento.

Mejores prácticas

  • Diseña las claves basándote en el orden que realmente necesites; recuerda que el orden es por partición.
  • Utiliza acks=all, min.insync.replicas=2 y un factor de replicación de tres para los topics importantes.
  • Habilita el productor idempotente y prefiere los commits de offset manuales.
  • Haz que los consumidores sean idempotentes, ya que el contrato predeterminado es “at-least-once”.
  • Mantén el procesamiento de max.poll.records por debajo de max.poll.interval.ms para evitar tormentas de rebalanceo.
  • Utiliza log compaction para el estado basado en claves y tombstones para las eliminaciones.
  • Monitorea el consumer lag por partición, no solo el total del cluster.
  • Trata los esquemas de los registros como un contrato versionado, utilizando un registro cuando haya más de un equipo involucrado.
  • Separa los topics por ciclo de vida y retención en lugar de volcar todo en uno solo.
  • Define la retención basándote en un requisito real; siete días es un valor predeterminado, no una política.
  • Prefiere Kafka gestionado a menos que ejecutarlo sea genuinamente el núcleo de tu negocio.
  • Utiliza Connect para integraciones estándar y Streams para el procesamiento dentro del cluster antes de escribir código personalizado.

Errores comunes

  • Asumir un orden global cuando Kafka solo garantiza el orden dentro de una partición.
  • Usar una clave aleatoria o nula para eventos que deben mantener su orden.
  • Hacer commit automático de los offsets antes de terminar el trabajo, perdiendo registros en caso de un crash.
  • Crear handlers que no sean idempotentes, duplicando efectos secundarios durante un rebalance.
  • Crear un topic con una sola partición y preguntarse por qué los consumers no pueden escalar.
  • Aumentar las particiones posteriormente y cambiar silenciosamente el enrutamiento de las claves.
  • Tratar a Kafka como una API de request/response con respuestas por cada mensaje.
  • Configurar max.poll.records con un valor demasiado alto y exceder max.poll.interval.ms.
  • Olvidar que la replicación multiplica los costes de almacenamiento y red.
  • No incluir el consumer lag en el dashboard hasta que el backlog tiene horas de retraso.
  • Ejecutar ZooKeeper en un cluster nuevo en 2026.

Próximos pasos

Kafka es el log central de un sistema orientado a eventos. El backend roadmap cubre la arquitectura orientada a eventos y los microservicios, donde se explica cómo encajan los eventos, productores y consumidores, y cómo un log se convierte en la columna vertebral entre servicios. Si estás comparando brokers, las guías de RabbitMQ y Redis Queues muestran las alternativas enfocadas primero en el enrutamiento y primero en el procesamiento de trabajos, respectivamente. Y dado que los consumidores son básicamente workers en segundo plano con offsets, la guía de Batch Processing cubre los reintentos, la idempotencia y la observabilidad que también se aplican en este caso.

En la practica

Producir, consumir, confirmar, administrar

Las cuatro operaciones que componen casi cualquier aplicación de Kafka.

producer.ts
import { Kafka } from "kafkajs";

const kafka = new Kafka({
  clientId: "orders",
  brokers: ["localhost:9092"],
});

const producer = kafka.producer({
  idempotent: true,
  maxInFlightRequests: 1,
});

await producer.connect();

await producer.send({
  topic: "orders.created",
  acks: -1, // wait for all in-sync replicas
  messages: [
    { key: order.customerId, value: JSON.stringify(order) },
  ],
});

await producer.disconnect();

Log de Kafka vs cola clásica

Una cola elimina un mensaje una vez confirmado. Un log de Kafka lo conserva, permitiendo que cualquier grupo reproduzca el historial y que muchos lectores compartan el mismo stream.

Log de Kafka
// A second group can read the same history
// from the beginning, months later.
await consumer.subscribe({
  topic: "orders.created",
  fromBeginning: true,
});
Cola clásica
// The message was acknowledged by the first
// worker and removed; it cannot be replayed
// or read by a second independent consumer.
await channel.ack(msg);

Confirmación automática vs manual

La confirmación automática funciona con un temporizador y puede avanzar sobre registros que aún se están procesando. Confirmar tras finalizar el trabajo hace que el modo de fallo sea explícito.

Manual
await consumer.run({
  autoCommit: false,
  eachMessage: async ({ topic, partition, message }) => {
    await handle(message);
    await consumer.commitOffsets([
      {
        topic,
        partition,
        offset: String(Number(message.offset) + 1),
      },
    ]);
  },
});
Automática
await consumer.run({
  // Offsets are committed on a timer, so a
  // crash can skip records that were never
  // processed.
  eachMessage: async ({ message }) => {
    await handle(message);
  },
});

Compromisos

¿Vale la pena la carga operativa de Kafka?

Kafka resuelve problemas que las colas no pueden, pero requiere infraestructura real y un modelo mental diferente. Elígelo por sus garantías, no por la moda.

Strengths

  • El replay lo cambia todo

    Como el historial se conserva, puedes inicializar un nuevo servicio desde el log, reconstruir una proyección tras corregir un bug y ejecutar analíticas sobre eventos raw mucho tiempo después de que ocurrieran.

  • Fan-out sin coordinación

    Cualquier número de consumer groups independientes leen el mismo topic a su propio ritmo. Añadir un lector no le cuesta nada al producer y nunca molesta a los consumers existentes.

  • Rendimiento con escalado horizontal

    Las escrituras secuenciales, el batching y las particiones permiten que un cluster pequeño absorba millones de registros por segundo; escalas añadiendo brokers y particiones.

Trade-offs

  • Es una plataforma, no una librería

    Los brokers, la replicación, los rebalances, el dimensionamiento de disco y el lag del consumer son responsabilidad tuya. Los servicios gestionados ayudan, pero Kafka nunca es una dependencia simple que puedas olvidar.

  • El enrutamiento no es su función

    Kafka no tiene exchanges ni claves de enrutamiento. Los consumers leen topics completos, por lo que el enrutamiento selectivo por mensaje debe integrarse en el diseño del topic o en el código de la aplicación.

  • Orden solo dentro de una partición

    No existe el orden global. Lograr el orden correcto implica elegir cuidadosamente las claves y el número de particiones, y no puedes añadir particiones después sin romper el enrutamiento por clave.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Apache Kafka?

Nuestro tutorial interactivo te guia a traves de Apache Kafka paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.