Fundamentos de MQTT

Mensajes retenidos en MQTT: cómo funcionan y cuándo usarlos

El flag retain permite que un broker MQTT entregue de inmediato a cada nuevo suscriptor el último valor conocido de un topic. Aprende cómo funciona, dónde brilla y dónde provoca errores por datos obsoletos.

Broker que almacena el último valor de cada topic para ilustrar los mensajes retenidos MQTT para nuevos suscriptores
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

Los mensajes retenidos MQTT resuelven un problema sencillo pero molesto: un cliente que se suscribe a un topic tiene que esperar a la siguiente publicación para saber algo. Con el flag retain, el broker guarda el último mensaje de ese topic y lo entrega de inmediato a cada nuevo suscriptor. En esta guía verás exactamente cómo se comportan los retained messages, los probarás en un servidor público y aprenderás cuándo usarlos y cuándo evitarlos.

Qué son los mensajes retenidos MQTT

Todo paquete PUBLISH de MQTT lleva un flag retain de un bit. Cuando un cliente publica con retain a true, el broker hace dos cosas: reenvía el mensaje a los suscriptores actuales como siempre y guarda una copia como retained message de ese topic exacto. Cada topic tiene como máximo un mensaje retenido, así que una nueva publicación retenida sustituye a la anterior.

Cuando más tarde un cliente se suscribe a un filtro que coincide con ese topic, el broker le envía el mensaje almacenado justo después del SUBACK. El suscriptor ve el flag retain activado en esa entrega, lo que le indica "este es el último valor conocido, no un evento nuevo". Las suscripciones con comodines también funcionan: suscribirse a factory/+/status devuelve de una vez el mensaje retenido de cada topic coincidente.

Algunas reglas que se pasan por alto con facilidad:

  • La retención es por topic, no por cliente. Cualquier cliente puede sobrescribir el valor retenido.
  • Publicar con retain y un payload vacío (de cero bytes) elimina el mensaje retenido.
  • Los mensajes reenviados a suscriptores ya conectados suelen llegar con el flag retain desactivado en MQTT 3.1.1. MQTT 5 añade la opción de suscripción "retain as published" para conservarlo.
  • La QoS de la entrega retenida es la menor entre la QoS del mensaje almacenado y la QoS de la suscripción.

Si necesitas repasar cómo enruta un broker los mensajes en general, lee antes qué hace un broker MQTT.

Cómo cambia la entrega el flag retain

La forma más fácil de entender la diferencia es comparar la misma publicación con y sin retain.

Escenario Retain false Retain true
Suscriptor conectado al publicar Recibe el mensaje Recibe el mensaje
Suscriptor que se conecta 5 minutos después No recibe nada hasta la siguiente publicación Recibe el último valor de inmediato
Segunda publicación en el mismo topic Se entrega, no se guarda nada Se entrega y sustituye el valor almacenado
Publicar un payload vacío Entrega un mensaje vacío Elimina el valor almacenado
Suscripción con comodín Solo mensajes futuros Último valor de cada topic coincidente

Por eso los retained messages se describen a menudo como el "último valor válido conocido". Convierten un flujo de eventos en algo que puedes consultar simplemente suscribiéndote.

Prueba los mensajes retenidos en el servidor público

Puedes probarlo en menos de un minuto con las herramientas de línea de comandos de Mosquitto contra el servidor MQTT público gratuito en broker.connectmq.com. Acepta conexiones anónimas con cualquier client ID no vacío. Usa un prefijo de topic único, porque todo lo que hay en el área pública lo puede leer cualquiera: sirve para pruebas y prototipos, no para datos de producción.

Primero publica un valor retenido y desconéctate:

mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-pub-r7k2 \
  -t connectmq/demo/r7k2/thermostat/setpoint \
  -m '{"setpoint":21.5,"unit":"C"}' -q 1 -r

Ahora suscríbete desde otra terminal. Aunque nadie esté publicando, el valor llega al instante. La opción -v imprime el topic y -F nos permite mostrar explícitamente el flag retain:

mosquitto_sub -h broker.connectmq.com -p 1883 -i demo-sub-r7k2 \
  -t 'connectmq/demo/r7k2/#' -F 'retained=%r %t %p'

Deberías ver retained=1 seguido del topic y el payload. Publica de nuevo sin -r con el suscriptor abierto y la nueva línea mostrará retained=0, porque es un mensaje en vivo y no uno almacenado.

Para eliminar el valor retenido, publica un mensaje retenido vacío. La opción -n envía un payload nulo:

mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-pub-r7k2 \
  -t connectmq/demo/r7k2/thermostat/setpoint -r -n

Vuelve a suscribirte y no llegará nada. Tratamos la limpieza con más detalle, incluidos scripts de limpieza con comodines, en cómo borrar un mensaje retenido.

Publicar mensajes retenidos desde Python

En el código de una aplicación, el flag retain es solo un parámetro. Este es un ejemplo completo con paho-mqtt 2.x que publica un estado de dispositivo retenido y luego lo lee con un cliente nuevo:

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

BROKER = "broker.connectmq.com"
PREFIX = f"connectmq/demo/{uuid.uuid4().hex[:6]}"
TOPIC = f"{PREFIX}/pump-07/state"

# 1. Publica un estado retenido y desconéctate
pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"pub-{uuid.uuid4().hex[:8]}")
pub.connect(BROKER, 1883, keepalive=30)
pub.loop_start()
info = pub.publish(TOPIC, json.dumps({"running": True, "rpm": 1480}), qos=1, retain=True)
info.wait_for_publish()
pub.loop_stop()
pub.disconnect()

# 2. Un nuevo suscriptor recibe el valor almacenado de inmediato
def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC, qos=1)

def on_message(client, userdata, msg):
    print(f"{msg.topic} retain={msg.retain} payload={msg.payload.decode()}")

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

El suscriptor imprime retain=True aunque se haya conectado cuando el publicador ya se había ido. En un dashboard o un gemelo digital 3D, eso significa que la interfaz puede mostrar el estado actual al cargar en lugar de aparecer en blanco.

Cuándo usar mensajes retenidos MQTT

Los retained messages funcionan mejor para datos que describen un estado actual más que un evento:

  1. Estado del dispositivo. Indicadores de conectado/desconectado, versión de firmware, modo de funcionamiento. Combinado con el Last Will, esto te da una presencia fiable; consulta el estado en línea de dispositivos con MQTT.
  2. Configuración y consignas. Un dispositivo que se reinicia se suscribe a su topic de configuración y recibe sus ajustes sin una llamada aparte a una API.
  3. Mediciones que cambian despacio. Un nivel de depósito que se actualiza cada 10 minutos no le sirve de nada a un dashboard recién abierto salvo que el último valor esté retenido.
  4. Metadatos de descubrimiento. Muchas herramientas domésticas e industriales publican descriptores retenidos para que los nuevos consumidores puedan encontrar los dispositivos.
  5. Estado inicial de un gemelo digital. Un gemelo necesita una imagen completa al arrancar, y los topics retenidos se la proporcionan con una única suscripción con comodín.

Cuándo no usar mensajes retenidos

El flag retain no es una base de datos ni una cola. Evítalo para:

  • Eventos y comandos. Un comando retenido de "abrir válvula" se volverá a enviar a cada dispositivo que se suscriba después, incluso tras un reinicio. Es un error clásico y peligroso. Los comandos no deberían ser retenidos y, a ser posible, deberían tener una expiración; consulta el message expiry interval de MQTT.
  • Historial de telemetría de alta frecuencia. Solo se guarda el último mensaje. Si necesitas historial, ejecuta un suscriptor que escriba en una base de datos de series temporales.
  • Alarmas que deben confirmarse. Una alarma retenida sigue visible después de haberse resuelto, salvo que algo la sobrescriba explícitamente.

Errores habituales y resolución de problemas

Valores obsoletos tras dar de baja un dispositivo. Los mensajes retenidos viven hasta que alguien los borra. Añade la limpieza a tu procedimiento de baja y publica un payload retenido vacío en cada topic que fuera del dispositivo.

Datos fantasma de pruebas. Durante el desarrollo es fácil dejar basura retenida en los topics. Un prefijo único por proyecto facilita mucho la limpieza con un suscriptor con comodín.

Reenvíos inesperados al volver a suscribirse. Cada SUBSCRIBE vuelve a desencadenar la entrega de los retenidos, lo que puede sorprender al código que se resuscribe al reconectarse. MQTT 5 ofrece la opción de suscripción retain handling: 0 envía los mensajes retenidos en cada suscripción, 1 solo si la suscripción es nueva y 2 nunca. Comprueba si tu librería cliente la expone.

El flag retain aparece a false en los mensajes en vivo. Es lo esperado en MQTT 3.1.1. Si tu lógica depende de saber si un mensaje se retuvo originalmente, usa MQTT 5 con retain as published.

Valores retenidos tras reiniciar el broker. Que los mensajes retenidos sobrevivan a un reinicio del broker depende de su configuración de persistencia. Diseña los dispositivos para que vuelvan a publicar su estado periódicamente o al reconectarse, de modo que un broker recién arrancado se repueble rápido, en lugar de dar por hecho que el almacén de retenidos es permanente.

El comportamiento está definido en la especificación OASIS MQTT 5.0 si quieres la redacción normativa exacta.

Mensajes retenidos en un broker MQTT gestionado

En un broker MQTT gestionado no configuras tú el almacenamiento de retenidos, pero el diseño de los topics sigue siendo cosa tuya. ConnectMQ admite retained messages en MQTT 3.1.1 y 5.0, y su portal incluye un explorador de topics en vivo y un cliente MQTT en el navegador, así que puedes suscribirte a tu prefijo y ver cómo llegan los valores retenidos en cuanto te conectas. Eso facilita detectar datos de prueba obsoletos o un dispositivo que nunca limpió lo que dejó.

Una checklist práctica antes de pasar a producción:

  1. Decide por topic: estado (retain) o evento (sin retain).
  2. Nunca retengas comandos.
  3. Vuelve a publicar el estado en cada reconexión, no solo cuando cambie.
  4. Limpia los topics retenidos cuando retires dispositivos.
  5. Mantén pequeños los payloads retenidos; cada nuevo suscriptor los recibe todos a la vez.

Para saber más del protocolo en su conjunto, el tutorial de MQTT recorre de principio a fin la publicación, la suscripción, la QoS y las sesiones, y la guía de broker MQTT en la nube explica cómo encajan los brokers alojados en un despliegue real.

Empieza con ConnectMQ

Crea un broker gratuito en app.connectmq.com con el plan Serverless, sin tarjeta de crédito. Obtendrás un usuario, una contraseña y un client ID en minutos, y podrás ver cómo aparecen tus topics retenidos en el explorador de topics de ConnectMQ en cuanto tu primer dispositivo publique.

Preguntas frecuentes

¿Cuántos mensajes retenidos puede tener un topic?

Exactamente uno. Una nueva publicación retenida en el mismo topic sustituye a la anterior, y un payload retenido vacío la elimina.

¿Caducan los mensajes retenidos?

No por defecto en MQTT 3.1.1. En MQTT 5 puedes establecer un message expiry interval en una publicación retenida, tras el cual el broker deja de entregarla a los nuevos suscriptores.

¿Afecta la QoS a los mensajes retenidos?

El mensaje retenido conserva la QoS con la que se publicó, y un nuevo suscriptor lo recibe con la menor entre esa QoS y la QoS de su propia suscripción.

¿Puedo ver qué topics tienen mensajes retenidos?

Suscríbete a un comodín amplio como your/prefix/# y se te entregará de inmediato cada mensaje retenido que haya debajo. En ConnectMQ puedes hacer lo mismo desde el cliente integrado en el navegador del portal.

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.