Ir al contenido

Brokers

Un bridge publica los eventos del ledger de una app en un bus de mensajes que ya tienes: NATS JetStream, RabbitMQ, Kafka o SQS. Es la opción cuando varios consumidores leen los mismos eventos, cuando quieres la retención y el fan-out del bus, o cuando tu equipo ya opera uno. Cada mensaje lleva el mismo sobre que un webhook.

Esos cuatro son los únicos brokers que el motor trae. Para otros destinos (Pusher, un archivo en S3, un bot), un conector tuyo consume por webhook, stream o bridge y hace el resto.

Un operador, en el panel del motor, en la ficha de tu app (sección Bridges). La API /v1 no tiene rutas de bridges: tu app no los crea ni los ve. Pídele al operador:

  1. Que agregue los hosts del broker a los destinos permitidos de la app (los mismos de los webhooks): los servidores que configuras y los que anuncia el broker (los otros nodos de un cluster de NATS, los listeners anunciados de Kafka).
  2. Que cree el bridge con su configuración y su secreto, y lo pruebe («Probar» conecta y comprueba el destino sin publicar nada).

Crear, editar, cambiar el secreto, pausar, borrar y reenviar es de un superadmin; ver, probar y revisar la DLQ, de cualquier operador. El secreto se guarda sellado y nunca vuelve a mostrarse.

Campo Qué es
nombre Único dentro de la app.
tipo nats, rabbitmq, kafka o sqs.
config Lo de cada broker (abajo). Sin contraseñas: van en el secreto.
secreto La contraseña, el token o la clave secreta.
instancias Vacío: todas las de la app.
tipos Tipos de evento. Vacío: todos.

Un bridge nuevo publica lo que llegue desde ahora, como una suscripción nueva: lo anterior no se manda (para eso está el replay).

Broker config Secreto Dónde publica
NATS JetStream servidores (nats://host:4222 o tls://…, hasta 10), subject (prefijo; por omisión motor), stream (opcional, para «Probar») Opcional: la contraseña de usuario, o un token si no hay usuario Subject <subject>.<instance_id>.<type>
RabbitMQ url (amqp://host:5672/vhost o amqps://…, sin usuario), exchange (por omisión motor.eventos), usuario Obligatorio: la contraseña Exchange topic, routing key <instance_id>.<type>, mensaje persistente y mandatory
Kafka servidores (host:9092), topic, sasl (ninguno, plain, scram-sha-256 o scram-sha-512), tls, usuario La contraseña de SASL (obligatoria con SASL) El topic, con clave = instance_id
SQS cola_url, region, endpoint (opcional, un compatible con SQS), usuario (el access key ID) Obligatorio: el secret access key La cola; en una .fifo, grupo = instance_id

Ejemplos de suscripción del lado del consumidor:

  • NATS: motor.> (todo), motor.*.message.received (un tipo de todas las instancias) o motor.INSTANCIA_ID.> (una instancia).
  • RabbitMQ: enlaza tu cola con # (todo), *.message.received o INSTANCIA_ID.#.

El cuerpo es el sobre del evento en JSON, idéntico al de un webhook. Los metadatos van en los encabezados o atributos de cada broker, para filtrar y deduplicar sin parsear el cuerpo:

Dato NATS (headers) RabbitMQ (propiedades y headers) Kafka (headers) SQS (atributos)
event_id Nats-Msg-Id y Motor-Event-Id message_id y motor-event-id motor-event-id motor-event-id (y MessageDeduplicationId en .fifo)
instance_id Motor-Instance-Id motor-instance-id La clave del mensaje motor-instance-id (y MessageGroupId en .fifo)
type Motor-Event-Type type motor-event-type motor-event-type
seq Motor-Seq motor-seq motor-seq motor-seq (Number)
Formato Content-Type: application/json content_type: application/json content-type: application/json —

Cada bridge lleva un checkpoint por instancia: el último seq que el broker confirmó. El motor publica los eventos siguientes en orden de seq y avanza el checkpoint solo con el ACK del broker (el PubAck de JetStream, el publisher confirm de RabbitMQ, el ACK de Kafka, la respuesta de SQS).

  • Al menos una vez. Si el motor se cae entre el ACK y el avance del checkpoint, el mismo evento sale otra vez con el mismo event_id. Deduplica por event_id, igual que con un webhook.
  • Sin copias en memoria. Un mensaje sin ACK es un error y se vuelve a leer del ledger: el motor no guarda nada en un buffer de reconexión.
  • Lo que dice el alcance vigente. Cada evento se relee del ledger justo antes de publicarlo: si su contenido venció, sale redactado; si cambias las instancias o los tipos del bridge, vale lo nuevo.
Error Qué hace el motor
Sin conexión, timeout o destino_inexistente El evento en cabeza de esa instancia reintenta con backoff: 1 s, 5 s, 30 s, 2 min, 10 min y luego cada hora. Los eventos siguientes de esa instancia esperan.
El broker rechaza ese mensaje (rechazado_por_broker: un tamaño, un nack) Va a la DLQ enseguida y el checkpoint sigue con el siguiente.
Pasa la vida máxima reintentando (72 h; bridges.horas_vida, de 1 a 720) Va a la DLQ y el checkpoint sigue.
El broker rechaza la credencial (credencial_rechazada) El bridge queda suspendido. Al reactivarlo, sigue desde su checkpoint.

Cada intento espera la conexión y el ACK hasta 10 s (bridges.timeout_segundos). Un broker caído no frena a los demás bridges ni a los webhooks: tienen su propio trabajo.

La DLQ de un bridge la ve el operador en el panel. Desde ahí reenvía toda la DLQ, algunas entregas o un rango de hasta 10 000 seq de una instancia; lo reenviado sale fuera del orden normal y con sus reintentos. Nada de eso vuelve a mandar algo a WhatsApp.

Estado del bridge Qué significa
activo Publica.
pausado No publica; el checkpoint espera. No se pierde nada.
suspendido El broker rechazó la credencial. Lo reactiva un operador.
eliminado Ya no publica.

El panel muestra además el estado de la conexión (sin_probar, conectado o caido), el último error real del broker (saneado, sin el secreto), el atraso (eventos que el broker todavía no confirmó), las instancias que están reintentando y el tamaño de la DLQ.

Broker Orden por instancia Deduplicación del lado del broker
NATS JetStream Sí: el motor publica en orden y espera cada ACK. Sí, con Nats-Msg-Id dentro de la ventana de duplicados del stream.
RabbitMQ Sí, en cada cola que recibe los mensajes. No: deduplica por message_id.
Kafka Sí: la clave es el instance_id, así que una instancia va siempre a la misma partición. No: deduplica por motor-event-id.
SQS .fifo Sí: MessageGroupId es el instance_id. Sí, con MessageDeduplicationId (5 minutos, la ventana de SQS).
SQS estándar No: SQS no garantiza el orden. No. Ordena por motor-seq y deduplica por motor-event-id en tu consumidor.

Entre instancias distintas no hay orden en ningún broker. Lo reenviado desde la DLQ o por un rango sale fuera del orden normal: usa seq para ubicarlo.