Fundamentos de MQTT

Garantías de orden de mensajes en MQTT explicadas

MQTT mantiene el orden de los mensajes, pero solo dentro de reglas muy concretas. Aprende exactamente qué garantiza la especificación, las situaciones reales que desordenan los mensajes y cómo los números de secuencia hacen robustos a tus consumidores.

Mensajes numerados que fluyen en secuencia a través de un broker, ilustrando las garantías de orden de mensajes MQTT
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

El orden de mensajes MQTT está garantizado, pero solo dentro de unos límites concretos que muchos desarrolladores nunca llegan a leer. Los mensajes de un publicador en un topic con un mismo nivel de QoS llegan en orden; casi todo lo demás no está prometido. Este artículo explica exactamente qué garantiza la especificación, las situaciones habituales en las que los mensajes aparecen desordenados y cómo añadir números de secuencia para que tus consumidores puedan detectarlo y gestionarlo.

Qué garantiza la especificación de MQTT

Tanto MQTT 3.1.1 como MQTT 5 definen un "Ordered Topic". Por defecto, un broker debe tratar cada topic como ordenado, lo que da lugar a esta garantía:

Los mensajes publicados por un cliente en un topic, con el mismo nivel de QoS, se entregan a cada suscriptor en el orden en que se publicaron.

La especificación también exige que, cuando un cliente reenvía tras una reconexión los mensajes QoS 1 y QoS 2 no confirmados, lo haga en el orden original, y que las confirmaciones se envíen en el orden en que se recibieron los paquetes.

Lee esa garantía con atención y los huecos saltan a la vista. No dice nada sobre:

  • Mensajes de distintos publicadores en el mismo topic.
  • Mensajes de un publicador en distintos topics.
  • Mensajes de un publicador en el mismo topic pero con distintos niveles de QoS.
  • Lo que hace tu aplicación después de que la librería cliente le entregue el mensaje.

Dónde se rompe el orden de mensajes MQTT

Situación ¿Orden garantizado? Por qué
Un publicador, un topic, misma QoS Sí La regla del Ordered Topic
Un publicador, dos topics No Cada topic se ordena de forma independiente
Dos publicadores, un topic No No hay reloj global; manda el orden de llegada al broker
Mismo topic, QoS 0 y QoS 1 mezcladas No Caminos de entrega y reintentos distintos
QoS 0 con una conexión caída Se mantiene el orden, pero con huecos Los mensajes perdidos no se reenvían
QoS 1 con reentrega Pueden aparecer duplicados Un mensaje reenviado puede llegar después de otros más nuevos a los que ya precedió
Suscripción compartida Sin orden global Los mensajes se reparten entre consumidores
Consumidor multihilo No Tu pool de workers reordena el procesamiento

Dos de estos casos merecen una mirada más atenta.

Suscripciones compartidas. Con $share/group/topic, el broker reparte los mensajes entre varios consumidores para balancear la carga. Cada consumidor ve un subconjunto y todos procesan en paralelo, así que no existe un orden global con sentido. Si importa el orden por dispositivo, diseña el sistema para que los mensajes de cada dispositivo lleguen siempre al mismo consumidor, o reordénalos más adelante. Nuestra guía de suscripciones compartidas en MQTT cubre ese compromiso.

Reentregas y duplicados. QoS 1 es al menos una vez. Si se pierde una confirmación, el emisor retransmite con el flag DUP activado. Un suscriptor puede entonces volver a ver el mensaje 5 después de haber procesado ya el 6 y el 7. Es un duplicado y no un verdadero desorden, pero a un código ingenuo le parece que el tiempo ha retrocedido. La solución se explica en cómo gestionar los mensajes duplicados en MQTT.

Demostrar el orden en el servidor público

El experimento más rápido usa el servidor MQTT público gratuito en broker.connectmq.com. Ten en cuenta su límite de 10 mensajes por segundo por cliente; por eso el publicador de abajo se autolimita.

Suscriptor:

mosquitto_sub -h broker.connectmq.com -p 1883 -i ord-sub-h5d3 -q 1 \
  -t 'connectmq/demo/h5d3/#' -v

Publicador, un topic, QoS 1, mensajes numerados:

for i in $(seq 1 20); do
  mosquitto_pub -h broker.connectmq.com -p 1883 -i ord-pub-h5d3 -q 1 \
    -t connectmq/demo/h5d3/seq -m "$i"
  sleep 0.2
done

Verás del 1 al 20 en orden. Ten en cuenta que cada llamada a mosquitto_pub es una conexión distinta, así que aquí es el bucle secuencial lo que mantiene el orden. Ahora ejecuta dos bucles en paralelo sobre dos topics distintos, seq-a y seq-b. Cada topic se mantiene en orden, pero las líneas de ambos topics se intercalan de forma impredecible. No hay ningún fallo: el orden entre topics simplemente no forma parte del contrato.

Añadir números de secuencia

Cuando el orden importa a tu aplicación, transpórtalo en el payload. Un número de secuencia monótono por publicador, junto con una marca de tiempo, permite a los consumidores detectar huecos, duplicados y mensajes obsoletos. Este es un par completo de publicador y consumidor con paho-mqtt 2.x:

import json
import time
import uuid
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/h5d3/pump-07/telemetry"
last_seq = {}  # dispositivo -> secuencia más alta vista

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC, qos=1)

def on_message(client, userdata, msg):
    data = json.loads(msg.payload)
    dev, seq = data["device"], data["seq"]
    prev = last_seq.get(dev)
    if prev is not None and seq <= prev:
        print(f"stale or duplicate seq={seq} (last={prev}), dropped")
        return
    if prev is not None and seq > prev + 1:
        print(f"gap: missing {prev + 1}..{seq - 1}")
    last_seq[dev] = seq
    print(f"apply seq={seq} value={data['value']}")

sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"ord-c-{uuid.uuid4().hex[:6]}")
sub.on_connect = on_connect
sub.on_message = on_message
sub.connect(BROKER, 1883, keepalive=30)
sub.loop_start()
time.sleep(1)

pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"ord-p-{uuid.uuid4().hex[:6]}")
pub.connect(BROKER, 1883, keepalive=30)
pub.loop_start()
for seq in [1, 2, 3, 5, 4, 6]:  # el 4 se envía tarde a propósito
    body = {"device": "pump-07", "seq": seq, "ts": time.time(), "value": 20 + seq}
    pub.publish(TOPIC, json.dumps(body), qos=1).wait_for_publish()
    time.sleep(0.2)

time.sleep(2)
for c in (pub, sub):
    c.loop_stop()
    c.disconnect()

El consumidor aplica el 1, el 2 y el 3, informa de un hueco antes del 5, descarta el 4 tardío por obsoleto y luego aplica el 6. Si conviene descartar, almacenar en búfer o aceptar los mensajes tardíos depende de los datos, lo que nos lleva a la estrategia.

Elegir una estrategia de orden

  1. Gana el último valor (estado). Para lecturas como la temperatura, descarta todo lo que sea más antiguo que lo que ya tienes. El código anterior hace esto.
  2. Búfer de reordenación (flujos de eventos). Retén los mensajes brevemente y libéralos en orden de secuencia, renunciando a los huecos tras un timeout. Útil cuando cada evento importa, como en los contadores.
  3. Upserts idempotentes (almacenamiento). Escribe cada mensaje con una clave formada por el dispositivo y la secuencia o la marca de tiempo. La base de datos acaba siendo correcta sea cual sea el orden de llegada.
  4. Un topic por flujo ordenado. Si los comandos deben aplicarse en orden, publícalos en un único topic, desde un único cliente y con una única QoS.
  5. Evita mezclar QoS en un mismo flujo. Elige una QoS por topic.

El orden en reconexiones y periodos sin conexión

Las reconexiones son donde más a menudo se ponen a prueba las suposiciones sobre el orden. Cuando vuelve un suscriptor con sesión persistente, el broker primero reenvía los mensajes que estaban en tránsito sin confirmar, luego entrega los mensajes encolados mientras el cliente estaba desconectado y después continúa con el tráfico en vivo. Dentro de un topic y de un publicador, el orden se mantiene, pero el consumidor recibe de repente una ráfaga de mensajes antiguos seguida de los actuales.

Aquí ayudan dos cosas. Primero, mantén secuencial el procesamiento por dispositivo, para que la ráfaga se aplique en orden en lugar de que la procesen en carrera varios workers en paralelo. Segundo, usa las marcas de tiempo del payload para decidir cómo presentar los datos atrasados: un dashboard puede querer saltar directamente al último valor, mientras que un historiador debería guardar cada lectura con su hora original. Si los mensajes encolados solo son útiles durante un tiempo limitado, la expiración de mensajes de MQTT 5 mantiene fuera de la ráfaga los muy antiguos.

En el lado del publicador, los dispositivos que almacenan lecturas localmente durante un corte de red deberían enviar el búfer en orden de secuencia antes de reanudar la publicación en vivo, y en el mismo topic y con la misma QoS que el tráfico normal.

Errores habituales con el orden y resolución de problemas

La secuencia se reinicia tras un reinicio. Un dispositivo que vuelve a empezar en seq 1 parece estar enviando datos obsoletos. Incluye un boot ID o un contador de época junto con la secuencia, o guarda el contador en la memoria flash.

El orden basado en relojes falla. Los relojes de los dispositivos derivan y a veces se reinician a 1970. Prefiere los números de secuencia para ordenar y las marcas de tiempo para mostrar.

El orden se pierde dentro de tu app. Los handlers asíncronos, los pools de hilos y las colas del consumidor reordenan libremente. Procesa el flujo de cada dispositivo en un único worker o particiona por ID de dispositivo.

Efectos de la ventana de mensajes en tránsito. El receive maximum de MQTT 5 permite tener varios mensajes QoS 1 en tránsito a la vez. El orden en la red se mantiene, pero los reintentos con pérdida de paquetes hacen más probables los duplicados. Consulta el artículo sobre el receive maximum de MQTT para ajustarlo.

El orden en un broker MQTT gestionado

ConnectMQ sigue las reglas de orden de MQTT por topic para clientes MQTT 3.1.1 y 5.0. Las suscripciones compartidas del plan Serverless (Paid) te dan escalado horizontal para los consumidores, con el compromiso de orden habitual descrito antes. Usar un broker MQTT gestionado no cambia las garantías del protocolo, pero elimina el trabajo operativo para que puedas concentrarte en el diseño de los consumidores. Para el contexto más amplio del protocolo, consulta el tutorial de MQTT.

Empieza con ConnectMQ

Crea un broker en el plan Serverless gratuito en app.connectmq.com, sin tarjeta de crédito. Ejecuta el ejemplo de números de secuencia con tus propias credenciales de ConnectMQ, observa el flujo en el explorador de topics en vivo del portal y luego llévalo a producción en un broker MQTT en la nube que escala con tu flota.

Preguntas frecuentes

¿Garantiza MQTT el orden de los mensajes?

Sí, para los mensajes de un cliente en un topic con la misma QoS. No hay garantía entre topics, entre publicadores ni con niveles de QoS mezclados.

¿Garantiza QoS 2 el orden?

QoS 2 garantiza una entrega exactamente una vez en cada salto. El orden sigue la misma regla por topic y por publicador.

¿Por qué veo un mensaje antiguo después de uno más nuevo?

Normalmente se debe a una reentrega QoS 1, a un segundo publicador o al procesamiento en paralelo en tu consumidor. Los números de secuencia permiten saber fácilmente cuál es la causa.

¿Están ordenadas las suscripciones compartidas?

No globalmente. Cada mensaje va a un miembro del grupo, y los miembros procesan en paralelo.

Artículos relacionados

¿Quién inventó MQTT? La historia, de los oleoductos al IoT

MQTT lo inventaron en 1999 Andy Stanford-Clark, de IBM, y Arlen Nipper, de Arcom, para monitorizar oleoductos vía satélite. Sigue su camino hacia el open source, OASIS, ISO y los brokers MQTT gestionados en la nube de hoy.

8 min de lectura

Por qué tu cliente MQTT se desconecta constantemente (y cómo solucionarlo)

Los bucles de reconexión son uno de los problemas más habituales de MQTT. Conoce las cinco causas típicas, desde los client ID duplicados hasta los timeouts de keep alive y los paquetes demasiado grandes, y sigue un flujo de diagnóstico que localiza la tuya.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.