Los mensajes offline en MQTT son las publicaciones que un broker retiene para un suscriptor temporalmente desconectado y que entrega en cuanto vuelve. Este artículo explica con exactitud qué mensajes cumplen los requisitos, qué límites se aplican en los brokers reales y te guía por una prueba reproducible para que veas tú mismo cómo se encolan y se entregan.
Las cuatro condiciones de los mensajes offline en MQTT
El encolado no es automático. Un broker encola un mensaje para un cliente offline solo cuando se cumplen todas estas condiciones:
| Condición | Qué comprobar |
|---|---|
| El cliente tiene una sesión persistente | MQTT 3.1.1: cleanSession = false. MQTT 5: Clean Start = false y Session Expiry Interval > 0 |
| La sesión no ha caducado | La caducidad de MQTT 5 aún no ha vencido y no se han superado los límites del broker |
| El cliente ya estaba suscrito a un filtro de topic coincidente | La suscripción debe existir antes de que se quedara offline |
| El QoS efectivo es 1 o 2 | El QoS de entrega es el menor entre el QoS de publicación y el de suscripción |
Si falla cualquiera de las condiciones, el mensaje sencillamente no se entrega a ese cliente. El publicador no recibe ningún error, porque MQTT desacopla por diseño a publicadores y suscriptores. Por eso son tan habituales los fallos del tipo "mi dispositivo se perdió un comando": el publicador ve un PUBACK correcto del broker, pero el broker no tenía ningún motivo para conservar el mensaje para ese suscriptor concreto.
¿Y QoS 0?
QoS 0 es "como máximo una vez". La especificación permite que un broker encole mensajes QoS 0 para sesiones offline, pero no lo exige, y la mayoría de los brokers no lo hacen. Trata QoS 0 como algo solo en directo. Las diferencias entre niveles se explican en detalle en los niveles de QoS de MQTT explicados.
¿Y los mensajes retenidos?
Los mensajes retenidos son un mecanismo aparte. El broker guarda el último mensaje retenido de cada topic y lo envía a cualquiera que se suscriba, tenga o no sesión. Resuelven "¿cuál es el valor actual?", no "¿qué me he perdido?". Un cliente que se reconecta con una sesión limpia sigue recibiendo el último valor retenido al suscribirse, pero ninguna de las actualizaciones intermedias. Un buen diseño suele usar ambos: mensajes retenidos para el estado y mensajes QoS 1 encolados para eventos y comandos.
Límites que se aplican a las colas offline
Todos los brokers ponen límites a las colas, porque de lo contrario un cliente desconectado que nunca vuelve consumiría memoria para siempre.
- Duración de la sesión. En MQTT 5, el session expiry interval decide cuánto sobrevive la cola. Cuando caduca, los mensajes encolados se descartan. En el servidor MQTT público gratuito broker.connectmq.com, pensado para pruebas y no para datos de producción, la caducidad de sesión está limitada a 30 minutos. Cómo elegir los valores se explica en el session expiry interval de MQTT.
- Profundidad de la cola. Los brokers limitan el número de mensajes o bytes encolados por sesión. Al alcanzar el límite, los mensajes nuevos se descartan o los antiguos se expulsan, según el broker.
- Caducidad del mensaje. Un publicador MQTT 5 puede fijar un Message Expiry Interval. Si un mensaje permanece en una cola más tiempo que ese, el broker lo descarta en lugar de entregar datos obsoletos.
- Tamaño del mensaje. Los límites de tamaño de mensaje del plan y del broker también se aplican a los mensajes encolados (64 KB en el broker público, 256 KB en el plan Serverless Free de ConnectMQ y 1 MB en el plan Serverless de pago).
- Reinicios del broker. Dónde residen las colas depende del broker. ConnectMQ guarda las sesiones y sus colas en memoria: sobreviven a las desconexiones de los clientes durante la duración de sesión concedida, pero no se conservan tras un reinicio del broker. Los clientes deben detectar una sesión perdida (Session Present = 0 en el CONNACK), volver a suscribirse y apoyarse en mensajes retenidos o en una resincronización a nivel de aplicación para recuperar el estado.
Prueba reproducible: observa la entrega offline
Esta prueba lleva unos cinco minutos con las herramientas de línea de comandos de mosquitto. Sustituye q5t9 por tu propia cadena aleatoria y mantén payloads inofensivos: cualquiera puede leer el área pública.
- Crea la sesión. Conecta un suscriptor con un ID fijo, Clean Start a false, una caducidad de sesión de 15 minutos y QoS 1:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i offline-sub-q5t9 -c -x 900 -q 1 -v \
-t 'connectmq/demo/q5t9/cmd/#'
Desconéctate. Pulsa Ctrl+C.
Publica una mezcla de niveles de QoS mientras el suscriptor está ausente:
H="-h broker.connectmq.com -p 1883 -V mqttv5 -i offline-pub-q5t9"
mosquitto_pub $H -q 0 -t 'connectmq/demo/q5t9/cmd/a' -m 'qos0 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/b' -m 'qos1 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/c' -m 'short-lived' \
-D publish message-expiry-interval 5
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/other' -m 'not subscribed'
- Espera al menos 10 segundos y vuelve a ejecutar el comando del paso 1.
Deberías ver solo connectmq/demo/q5t9/cmd/b qos1 message. Este es el motivo por el que faltan los demás:
cmd/aera QoS 0, así que no se encoló.cmd/ctenía una caducidad de mensaje de 5 segundos y se descartó en la cola.otherno coincidía con el filtro de suscripción.
- Repite sin
-cen el paso 1. No se entrega nada al reconectar, porque la sesión se descartó al cerrarse la conexión.
El mismo patrón en Python
Un dispositivo que se reconecta y procesa todo lo que se perdió:
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
TOPIC = "connectmq/demo/q5t9/cmd/#"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("Connect failed:", reason_code)
return
print("Connected. Session present:", flags.session_present)
if not flags.session_present:
# Sesión nueva o perdida: suscríbete y recupera el estado por otra vía
client.subscribe(TOPIC, qos=1)
def on_message(client, userdata, msg):
print(f"Processing {msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="offline-py-q5t9",
protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.reconnect_delay_set(min_delay=1, max_delay=60)
props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 900
client.connect("broker.connectmq.com", 1883, keepalive=60,
clean_start=False, properties=props)
client.loop_forever()
Cómo diseñar pensando en los clientes offline
El encolado ayuda con las interrupciones cortas, pero no debería ser tu única herramienta de fiabilidad. Un diseño robusto combina varios mecanismos:
- Usa QoS 1 para comandos y eventos, y QoS 0 para telemetría de alta frecuencia en la que cada lectura sustituye a la anterior.
- Mantén una caducidad de sesión realista. Lo bastante larga para cubrir los cortes típicos y lo bastante corta para que los dispositivos muertos no acumulen colas.
- Fija la caducidad de mensaje en los comandos sensibles al tiempo, para que un dispositivo que vuelve al cabo de una hora no ejecute una instrucción obsoleta.
- Publica el estado como mensajes retenidos, para que cualquier cliente pueda resincronizarse al instante sin depender de una cola.
- Haz que los handlers sean idempotentes. QoS 1 puede entregar duplicados tras las reconexiones; incluye un ID de mensaje o un número de secuencia en el payload.
- Trata Session Present = 0 como un evento normal, no como un error.
- Espera una ráfaga al reconectar. Los clientes que han estado offline pueden recibir muchos mensajes de golpe; el Receive Maximum de MQTT 5 limita cuántos mensajes sin confirmar llegan en paralelo.
Para entender las sesiones en sí, lee las sesiones persistentes MQTT y el flag clean session. Si acabas de empezar, el tutorial de MQTT cubre primero los fundamentos de publicación/suscripción.
Resolución de problemas con mensajes offline que faltan
- No llega nada tras reconectar: confirma que el client ID es idéntico, que Clean Start es false y, en MQTT 5, que la caducidad de sesión no es cero.
- Llegan unos mensajes y otros no: compara el QoS de cada publicación con el de la suscripción y comprueba la caducidad de los mensajes.
- Los mensajes llegan dos veces: es lo esperado con QoS 1 tras una confirmación interrumpida; elimina duplicados en la aplicación.
- Session Present vale 0 inesperadamente: la sesión caducó, el client ID cambió o el broker se reinició.
Encola mensajes offline con ConnectMQ
ConnectMQ es un broker MQTT gestionado que admite sesiones persistentes, caducidad de sesión y caducidad de mensajes en MQTT 5.0, además de clientes MQTT 3.1.1. El plan Serverless Free admite QoS hasta 1, que es todo lo que requiere el encolado offline, y QoS 2 está disponible en los planes de pago. Crea tu broker gratuito sin tarjeta de crédito, conéctate con YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID, y ejecuta la prueba anterior contra tu propio endpoint privado. Si estás comparando proveedores, descubre qué buscar en un broker MQTT en la nube.
Preguntas frecuentes
¿Almacena MQTT los mensajes cuando el suscriptor está offline?
Solo si el suscriptor tiene una sesión persistente, ya estaba suscrito a un topic coincidente y el QoS efectivo es 1 o 2. En caso contrario, el mensaje no se conserva para ese cliente.
¿Por qué se pierden mis mensajes QoS 0 mientras el dispositivo está offline?
Los mensajes QoS 0 normalmente no se encolan para sesiones offline; la especificación lo deja como opcional. Publica y suscríbete con QoS 1 si el mensaje debe sobrevivir a una desconexión.
¿Cuánto tiempo conserva un broker los mensajes offline?
Hasta que caduca la sesión, se alcanza un límite de cola, vence la caducidad del propio mensaje o, si las sesiones se guardan en memoria, el broker se reinicia. En ConnectMQ las sesiones residen en memoria, y el servidor MQTT público gratuito limita la caducidad de sesión a 30 minutos.
¿Sustituyen los mensajes retenidos al encolado offline?
No. Un mensaje retenido proporciona a un nuevo suscriptor el último valor de un topic, mientras que el encolado offline entrega cada mensaje QoS 1 o 2 que el cliente se perdió. La mayoría de los sistemas usan ambos.



