El intervalo de expiración de mensajes MQTT (message expiry interval) es una propiedad de MQTT 5 que indica al broker durante cuántos segundos sigue siendo válido un mensaje. Una vez pasado ese tiempo, el broker descarta el mensaje en lugar de entregárselo a un cliente que se reconecta tarde. En este artículo aprenderás exactamente cómo funciona la expiración de mensajes MQTT, verás cómo descarta un comando obsoleto en una prueba real y obtendrás una checklist para elegir valores de expiración en tu propio sistema.
El problema: comandos que llegan demasiado tarde
Imagina un dispositivo suscrito con QoS 1 y una sesión persistente. Un operador envía "abrir puerta" mientras el dispositivo está desconectado. El broker encola el comando porque la sesión sigue viva. Dos horas después, el dispositivo se reconecta por un enlace celular inestable y el broker entrega diligentemente "abrir puerta". Ya no hay nadie esperando en la puerta.
Antes de MQTT 5 no había forma de evitarlo a nivel de protocolo. Los desarrolladores añadían marcas de tiempo a los payloads y confiaban en que todos los dispositivos las comprobaran. MQTT 5 llevó la solución al protocolo con el message expiry interval, una propiedad del paquete PUBLISH. Es una de las funciones de MQTT 5 más prácticas para cualquiera que controle equipos físicos.
Cómo funciona el intervalo de expiración de mensajes MQTT
El publicador establece la propiedad en segundos, como un entero de cuatro bytes. El broker aplica entonces estas reglas:
- Cuando llega el mensaje, el broker registra la hora.
- Si el mensaje está en cola para un suscriptor desconectado con sesión persistente y el intervalo vence antes de la entrega, el broker lo descarta.
- Cuando el broker entrega el mensaje, reescribe la propiedad con el tiempo restante. Un mensaje publicado con 60 segundos y entregado 45 segundos después llega con un intervalo de 15.
- Si el mensaje es retained, la copia retenida también expira. Pasado el intervalo, los nuevos suscriptores ya no la reciben.
- Si la propiedad no está presente, el mensaje nunca expira, que es el comportamiento de MQTT 3.1.1.
La cuarta regla hace que la expiración sirva para algo más que los comandos. Un aviso retenido de "modo mantenimiento activo" puede configurarse para que expire por sí solo, de modo que un indicador olvidado no se quede ahí durante semanas.
| Tipo de mensaje | Expiración sugerida | Motivo |
|---|---|---|
| Comando de actuador (abrir, arrancar, parar) | De 5 a 60 s | Solo es válido mientras un operador está pendiente |
| Envío de configuración | De horas a días | Sigue siendo útil cuando un dispositivo se reconecta más tarde |
| Telemetría hacia un consumidor persistente | Minutos | Las lecturas antiguas pierden valor, pero pueden rellenar huecos |
| Estado temporal retenido | La duración del estado | Se borra solo automáticamente |
| Notificación de alarma | Hasta que deja de requerir acción | Evita avalanchas de alarmas desfasadas |
Por qué la expiración necesita sesiones persistentes para importar
La expiración solo afecta a los mensajes que esperan en algún sitio. En una sesión limpia sin estado almacenado, un suscriptor desconectado simplemente se pierde los mensajes, así que no hay nada que expirar. La expiración importa en dos lugares:
- Colas de sesión. Un cliente que se conecta con clean start a false y un session expiry interval mayor que cero tiene sus mensajes QoS 1 y QoS 2 en cola mientras está desconectado. Consulta el session expiry interval de MQTT, que explica cómo se construye esa cola.
- Almacenamiento de retenidos. Un mensaje retenido con expiración se elimina tras el intervalo.
Fíjate en la diferencia entre ambos ajustes de expiración: la expiración de sesión controla cuánto tiempo recuerda el broker a un cliente; la expiración de mensaje controla durante cuánto tiempo merece la pena entregar un mensaje concreto. Normalmente querrás ambos.
Probar la expiración de mensajes en un broker real
Usaremos el servidor MQTT público gratuito en broker.connectmq.com, que admite MQTT 5 y sesiones de hasta 30 minutos. Usa tu propio prefijo de topic y recuerda que es para pruebas y prototipos, no para datos de producción.
Paso 1: crea una sesión persistente. Conéctate una vez con clean start desactivado (-c), una expiración de sesión de 600 segundos (-x 600) y QoS 1, y luego detén el proceso con Ctrl+C:
mosquitto_sub -V mqttv5 -h broker.connectmq.com -p 1883 -i exp-dev-m4t9 \
-c -x 600 -q 1 -t connectmq/demo/m4t9/gate/cmd -v
Paso 2: mientras el suscriptor está desconectado, publica un comando que expira en 10 segundos y otro que dura 5 minutos:
mosquitto_pub -V mqttv5 -h broker.connectmq.com -p 1883 -i exp-op-m4t9 -q 1 \
-t connectmq/demo/m4t9/gate/cmd -m 'open (10s)' \
-D publish message-expiry-interval 10
mosquitto_pub -V mqttv5 -h broker.connectmq.com -p 1883 -i exp-op-m4t9 -q 1 \
-t connectmq/demo/m4t9/gate/cmd -m 'report-status (300s)' \
-D publish message-expiry-interval 300
Paso 3: espera al menos 20 segundos y vuelve a conectarte exactamente con el mismo comando del paso 1. Solo llega report-status (300s). El comando de apertura expiró en la cola y nunca se entregó. Repite los pasos 2 y 3 rápidamente, en menos de 10 segundos, y llegarán ambos mensajes.
Configurar la expiración en Python y Node.js
En paho-mqtt 2.x se adjunta un objeto Properties a la publicación. El cliente debe usar MQTT 5:
import json
import uuid
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"op-{uuid.uuid4().hex[:8]}",
protocol=mqtt.MQTTv5,
)
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()
props = Properties(PacketTypes.PUBLISH)
props.MessageExpiryInterval = 30 # segundos
cmd = {"action": "open", "issuedBy": "operator-3"}
info = client.publish("connectmq/demo/m4t9/gate/cmd", json.dumps(cmd), qos=1, properties=props)
info.wait_for_publish()
client.loop_stop()
client.disconnect()
En el lado receptor, msg.properties contiene MessageExpiryInterval con los segundos restantes, que un dispositivo puede registrar o usar para rechazar comandos a los que casi no les queda tiempo.
Con MQTT.js v5 la propiedad va en las opciones de publicación:
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "op-" + Math.random().toString(16).slice(2, 10),
protocolVersion: 5,
});
client.on("connect", () => {
client.publish(
"connectmq/demo/m4t9/gate/cmd",
JSON.stringify({ action: "close" }),
{ qos: 1, properties: { messageExpiryInterval: 30 } },
(err) => {
if (err) console.error(err);
client.end();
}
);
});
Errores habituales y resolución de problemas
La expiración se ignora por completo. Comprueba que tanto el publicador como la conexión con el broker usan MQTT 5. Un cliente 3.1.1 no puede enviar la propiedad, y un suscriptor 3.1.1 recibe el mensaje sin ella.
El mensaje sigue llegando tarde. La expiración impide la entrega desde la cola del broker. No ayuda si el mensaje ya está en el búfer del propio dispositivo o en un gateway intermedio. Los dispositivos que ejecutan comandos deberían seguir comparando una marca de tiempo del payload con su reloj cuando la seguridad importa.
Los comandos expiran demasiado rápido. Ten en cuenta la latencia de la red y de las colas del broker. Una expiración de 1 o 2 segundos puede descartar comandos legítimos en enlaces lentos.
Ha desaparecido un mensaje retenido. Si alguien publicó el valor retenido con expiración, desaparece cuando vence el intervalo. A menudo es intencionado; revisa el publicador antes de suponer una pérdida de datos. Para lo básico de la retención, consulta los retained messages de MQTT.
Expiración frente a QoS. La QoS controla si un mensaje se entrega; la expiración controla si todavía merece la pena entregarlo. Un mensaje QoS 1 con expiración puede, legítimamente, no llegar nunca.
Combinar la expiración con otras salvaguardas
La expiración de mensajes es una capa de defensa, no todo el diseño. Para comandos que mueven equipos físicos, una configuración robusta suele combinar varios mecanismos:
- Expiración en todos los comandos, para que el broker elimine de las colas los obsoletos.
- Un ID de comando y una marca de tiempo de emisión en el payload, para que el dispositivo pueda rechazar cualquier cosa más antigua que su propio umbral, aunque un gateway la haya retenido en búfer.
- Sin flag retain en los comandos, para que un reinicio nunca vuelva a ejecutar una instrucción antigua.
- Un topic de confirmación en el que el dispositivo informe de si ejecutó o rechazó el comando, idealmente usando el patrón de petición/respuesta de MQTT.
- Enclavamientos en el dispositivo para todo lo relacionado con la seguridad. La red nunca debería ser lo único que impide una acción insegura.
Piensa en la expiración como la herramienta que mantiene limpias las colas y hace que las entregas tardías sean raras, mientras que las comprobaciones en el dispositivo hacen imposible una ejecución tardía.
Expiración de mensajes en ConnectMQ
ConnectMQ admite MQTT 5.0 con message expiry, session expiry, response topic y el resto de propiedades de MQTT 5 en sus conexiones MQTT 5.0. En producción, conecta tus herramientas de operador y tus dispositivos con las credenciales del portal por TLS en el puerto 8883 y establece como política una expiración en cada topic de comandos. Un broker MQTT gestionado se encarga de operar el almacén de sesiones y las colas, así que tu equipo solo tiene que elegir valores de expiración razonables. El tutorial de MQTT cubre el resto de los fundamentos.
Empieza con ConnectMQ
Crea un broker gratis en app.connectmq.com con el plan Serverless, sin tarjeta de crédito. Conéctate con MQTT 5, añade una expiración a tu primer comando y pruébalo con un dispositivo que se reconecte tarde. Hacerlo en un broker MQTT en la nube significa no tener servidores que mantener mientras experimentas.
Preguntas frecuentes
¿Qué unidad usa el intervalo de expiración de mensajes?
Segundos, almacenados como un entero sin signo de cuatro bytes. Si la propiedad no se establece, el mensaje no expira.
¿Ve el suscriptor el valor de expiración original?
No. El broker envía el tiempo restante, así que un suscriptor puede saber cuánta validez le queda al mensaje cuando llega.
¿Funciona la expiración de mensajes con MQTT 3.1.1?
No. Es una propiedad de MQTT 5. Con MQTT 3.1.1 necesitas una marca de tiempo en el payload que el receptor compruebe por sí mismo.
¿Pueden expirar los mensajes retenidos?
Sí. Un mensaje retenido publicado con un intervalo de expiración se elimina del almacenamiento de retenidos una vez que vence el intervalo.



