El session expiry interval de MQTT indica al broker cuánto tiempo debe conservar la sesión de un cliente después de que este se desconecte y, junto con el flag Clean Start, sustituye al flag clean session de MQTT 3.1.1, que funcionaba a todo o nada. En este artículo aprenderás cómo se combinan ambos ajustes, cómo puede cambiarse el intervalo en el momento de la desconexión y cómo elegir valores sensatos para sensores, gateways y servicios backend.
Por qué MQTT 5 sustituyó el flag clean session
En MQTT 3.1.1, cleanSession hacía dos trabajos con un solo bit. Ponerlo a false significaba "reanuda mi sesión antigua y conserva esta para siempre". No había forma de decir "reanuda mi sesión, pero olvídala si desaparezco más de una hora". Los brokers lo resolvían con ajustes de caducidad en el servidor, lo que hacía que el comportamiento variara entre brokers y que se acumularan sesiones abandonadas.
MQTT 5 separa ambas cuestiones:
- Clean Start (un flag del CONNECT) responde a: ¿debe el broker descartar ahora mismo cualquier sesión existente?
- Session Expiry Interval (una propiedad del CONNECT, en segundos) responde a: ¿cuánto debe vivir la sesión después de cerrarse la conexión de red?
El diseño se describe en la especificación OASIS MQTT 5.0 y es una de las mejoras más prácticas que repasamos en nuestra visión general de las novedades de MQTT 5.
Cómo se combinan Clean Start y Session Expiry
| Clean Start | Session Expiry Interval | Comportamiento | Equivalente en 3.1.1 |
|---|---|---|---|
| true | 0 (o ausente) | Sesión nueva, eliminada al desconectar | cleanSession = true |
| false | 0 (o ausente) | Reanuda la sesión existente si la hay, eliminada al desconectar | Sin equivalente directo |
| true | > 0 | Descarta la sesión antigua e inicia una nueva que sobrevive N segundos a la desconexión | Sin equivalente directo |
| false | > 0 | Reanuda si existe y sobrevive N segundos a la desconexión | cleanSession = false (con un timeout) |
| false | 0xFFFFFFFF | Reanuda si existe y nunca caduca | cleanSession = false |
En la práctica importan algunos detalles:
- El temporizador arranca cuando se cierra la conexión de red, no cuando el cliente publicó por última vez.
- Si el cliente se reconecta antes de que venza el temporizador, la sesión se reanuda y el temporizador se cancela.
- Cuando transcurre el intervalo, el broker elimina las suscripciones y los mensajes encolados.
- Si la sesión caduca, el mensaje de will se envía como muy tarde en ese momento, aunque el will delay interval sea más largo.
Cambiar el intervalo al desconectar
MQTT 5 permite a un cliente actualizar la caducidad en su paquete DISCONNECT. Un dispositivo que entra en un mantenimiento planificado puede decir "conserva mi sesión durante un día" al marcharse, aunque normalmente funcione con un valor más corto. Hay una restricción: si el cliente se conectó con una caducidad de 0, no puede fijar un valor distinto de cero al desconectar; el broker lo trata como un error de protocolo.
El broker puede imponerse
Un broker puede negarse a conservar las sesiones tanto tiempo como pides. En ese caso devuelve una propiedad Session Expiry Interval en el CONNACK, y ese es el valor vigente. Registra siempre las propiedades del CONNACK durante el desarrollo para conocer el número real. En el servidor MQTT público gratuito broker.connectmq.com, pensado para pruebas y prototipos, la caducidad de sesión está limitada a 30 minutos.
Cómo elegir el session expiry interval de MQTT
Piensa en dos preguntas: ¿cuánto tiempo puede estar realmente offline este cliente? y ¿qué antigüedad puede tener un mensaje encolado antes de que deje de ser útil?
| Tipo de cliente | Caducidad sugerida | Motivo |
|---|---|---|
| Dashboard en directo, pestaña del navegador | 0 | Solo importan los datos actuales; los mensajes retenidos llenan la pantalla al conectar |
| Sensor conectado a la red eléctrica que solo publica | De 0 a 300 s | Poco estado de sesión; una caducidad corta limpia rápido |
| Dispositivo que recibe comandos | De 1 a 24 h | Los comandos deben sobrevivir a caídas de Wi-Fi y cortes breves |
| Dispositivo con batería que despierta cada hora | Algo más que el intervalo de despertar | La sesión debe durar más que el periodo de reposo |
| Worker backend que procesa alarmas | Horas, ajustadas a tu tiempo de recuperación | Las alarmas perdidas deben procesarse tras los redespliegues |
| Cliente dado de baja o de pruebas | 0 | Evita dejar sesiones huérfanas |
Combina los intervalos de caducidad largos con la propiedad Message Expiry Interval de MQTT 5 en las publicaciones sensibles al tiempo. Un comando "arrancar bomba" que pasa seis horas en una cola puede ser más peligroso que uno que nunca se entrega.
Un ejemplo práctico: sensor dormilón con comandos
Imagina un sensor con batería que despierta cada 15 minutos, publica una lectura, comprueba si hay comandos de configuración y vuelve a dormir. Si su caducidad de sesión fuera 0, cualquier comando enviado mientras dormía se descartaría, porque no habría sesión en la que encolarlo. Si la caducidad fuera infinita, un sensor que muriera en campo dejaría una sesión que seguiría acumulando comandos que nadie leerá. Un valor de unos 20 a 30 minutos cubre un despertar perdido más algo de variabilidad de red, y la sesión desaparece por sí sola cuando el dispositivo ya no existe de verdad. Envía los comandos con QoS 1 y una caducidad de mensaje de unas pocas horas, y el dispositivo recibirá todo lo que siga siendo relevante la próxima vez que se conecte.
Cómo configurar la caducidad de sesión en código
Todos los ejemplos usan un prefijo de demostración único en el servidor MQTT público gratuito. Sustituye s4e8 por tu propia cadena aleatoria; el área pública es visible para todo el mundo.
mosquitto_sub
# Reanuda (o crea) una sesión que sobrevive 10 minutos tras la desconexión
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i expiry-demo-s4e8 -c -x 600 -q 1 -v -d \
-t 'connectmq/demo/s4e8/commands/#'
-c pone Clean Start a false y -x 600 fija el Session Expiry Interval. El flag -d imprime el CONNACK, así que puedes ver si el broker concedió el valor.
Python (paho-mqtt 2.x)
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
def on_connect(client, userdata, flags, reason_code, properties):
granted = getattr(properties, "SessionExpiryInterval", None)
print("Connected:", reason_code,
"| session present:", flags.session_present,
"| broker override:", granted)
if not flags.session_present:
client.subscribe("connectmq/demo/s4e8/commands/#", qos=1)
def on_message(client, userdata, msg):
print("Command:", msg.topic, msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="expiry-py-s4e8",
protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
connect_props = Properties(PacketTypes.CONNECT)
connect_props.SessionExpiryInterval = 600
client.connect("broker.connectmq.com", 1883, keepalive=60,
clean_start=False, properties=connect_props)
client.loop_start()
try:
input("Press Enter to disconnect with a 30 minute expiry...\n")
finally:
bye = Properties(PacketTypes.DISCONNECT)
bye.SessionExpiryInterval = 1800
client.disconnect(properties=bye)
client.loop_stop()
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "expiry-node-s4e8",
protocolVersion: 5,
clean: false, // Clean Start = false
properties: { sessionExpiryInterval: 600 },
});
client.on("connect", (connack) => {
console.log("session present:", connack.sessionPresent,
"props:", connack.properties);
if (!connack.sessionPresent) {
client.subscribe("connectmq/demo/s4e8/commands/#", { qos: 1 });
}
});
client.on("message", (topic, payload) => {
console.log(topic, payload.toString());
});
Errores habituales y cómo detectarlos
- Clean Start a false y caducidad 0. Los desarrolladores portan código de 3.1.1, ponen
clean: falsey se olvidan de la propiedad de caducidad. En MQTT 5 esa sesión se elimina en cuanto cae la conexión, así que nunca se encola nada. Si los mensajes encolados no aparecen nunca, compruébalo primero. - Caducidad infinita en clientes de prueba. Cada script desechable con un ID único y
0xFFFFFFFFdeja una sesión tras de sí. Usa 0 en los experimentos, salvo que estés probando sesiones. - Ignorar Session Present. Si el broker se reinició o la sesión caducó, Session Present vale 0 y tus suscripciones han desaparecido. Vuelve a suscribirte siempre en ese caso.
- Dar por hecha la durabilidad ante reinicios del broker. La caducidad de sesión describe cuánto tiempo puede vivir una sesión, no una garantía de almacenamiento. ConnectMQ guarda el estado de sesión en memoria: sobrevive a las desconexiones de los clientes durante el intervalo concedido, pero se borra si el propio broker se reinicia.
- Cambiar los client IDs. La caducidad está ligada al client ID. Un ID nuevo es una sesión nueva; consulta las buenas prácticas del client ID de MQTT.
Cómo encaja en el panorama general
La caducidad de sesión es la manera de MQTT 5 de usar sesiones persistentes con seguridad: obtienes encolado offline donde lo necesitas y limpieza automática donde no. Si todavía estás aprendiendo las piezas del protocolo, el tutorial completo de MQTT es una buena lectura complementaria.
Usa sesiones MQTT 5 en ConnectMQ
ConnectMQ es un broker MQTT gestionado con soporte completo de MQTT 5.0, incluidos la caducidad de sesión, la caducidad de mensajes, los alias de topic y los reason codes. Puedes probar combinaciones de Clean Start y caducidad con el cliente MQTT integrado en el navegador del portal o con tu propio código, y después llevar los mismos ajustes a producción. Empieza con el plan Serverless gratuito, sin tarjeta de crédito, y conéctate con tus YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID generados.
Preguntas frecuentes
¿Cuál es el session expiry interval por defecto en MQTT?
Si la propiedad no está presente, el session expiry interval es 0, lo que significa que la sesión termina cuando se cierra la conexión de red. Debes fijarlo explícitamente para obtener una sesión persistente en MQTT 5.
¿Qué diferencia hay entre Clean Start y clean session?
Clean session, en MQTT 3.1.1, controlaba tanto el descarte de la sesión antigua como la conservación de la nueva. Clean Start, en MQTT 5, solo controla el descarte en el momento de conectar, mientras que el Session Expiry Interval controla cuánto vive la sesión después.
¿Cuál es el session expiry interval máximo?
El máximo del protocolo es 4.294.967.295 segundos (0xFFFFFFFF), lo que significa que la sesión nunca caduca. Los brokers pueden imponer límites más bajos e indicarlos en el CONNACK; el servidor MQTT público gratuito de ConnectMQ permite hasta 30 minutos.
¿Puedo cambiar la caducidad de sesión al desconectarme?
Sí, enviando la propiedad Session Expiry Interval en el DISCONNECT, salvo que te conectaras originalmente con una caducidad de 0.



