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
- 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.
- 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.
- 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.
- 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.
- 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.



