Fundamentos de MQTT

Comodines MQTT: cómo usar + y # en los filtros de topic

Los comodines + (un nivel) y # (varios niveles) permiten que una sola suscripción MQTT cubra muchos topics. Conoce las reglas exactas de coincidencia, los casos límite y cómo probar cada filtro en un broker real.

Ilustración de los comodines MQTT con los símbolos más y almohadilla en un filtro de topic que coincide con ramas de un árbol de topics
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 comodines MQTT permiten que una sola suscripción coincida con muchos topics: + coincide con exactamente un nivel de topic y # coincide con cualquier número de niveles al final de un filtro. En esta guía aprenderás las reglas precisas de coincidencia, los casos límite que confunden a la gente y una serie de comandos mosquitto_sub que puedes ejecutar contra un broker público para ver cada filtro en acción.

Topics frente a filtros de topic

Los publicadores siempre publican en un nombre de topic concreto, como home/kitchen/temperature. Los suscriptores se suscriben con un filtro de topic, que puede contener comodines. El broker compara cada topic entrante con el filtro de cada suscripción, nivel a nivel, y entrega el mensaje a todas las coincidencias. Ese trabajo de enrutamiento es el núcleo de lo que hace un broker MQTT.

Los comodines solo son válidos en los filtros. Publicar en un topic que contenga + o # es un error de protocolo, y la mayoría de los brokers desconectarán al cliente.

El comodín de un solo nivel: +

+ coincide con exactamente un nivel, sea cual sea su contenido. Debe ocupar un nivel completo: home/+/temperature es válido, home/kit+/temperature no.

Filtro Coincide con No coincide con
home/+/temperature home/kitchen/temperature, home/garage/temperature home/temperature, home/kitchen/sensor1/temperature
+/+/status site1/pump3/status site1/status, site1/hall/pump3/status
home/+ home/kitchen home, home/kitchen/light
+ home home/kitchen, /home

Conviene recordar dos detalles. Primero, + también coincide con un nivel vacío, así que home/+/temperature coincide con home//temperature. Segundo, una barra inicial crea un primer nivel vacío, así que +/home coincide con /home. Ambos son buenos motivos para seguir unas convenciones de nombres de topics MQTT sensatas y evitar por completo los niveles vacíos.

Puedes usar varios + en un mismo filtro. factory/+/+/temperature coincide con todas las temperaturas situadas dos niveles por debajo de factory, lo que es ideal para dashboards que muestran una medida en toda una flota.

El comodín multinivel:

# coincide con cualquier número de niveles, incluido cero, y debe ser el último carácter del filtro, ocupando un nivel completo.

Filtro Coincide con No coincide con
home/# home, home/kitchen, home/kitchen/light/state office/kitchen
home/kitchen/# home/kitchen, home/kitchen/temperature home/garage/temperature
# Todos los topics que no empiezan por $ $SYS/...
home/+/light/# home/kitchen/light, home/kitchen/light/brightness home/light

Entre los filtros no válidos están home/#/temperature (# no es el último), home# (no ocupa un nivel completo) y home/kitchen#.

Ten en cuenta que home/# también coincide con el propio topic padre home. Si solo quieres los hijos, usa home/+/#, que exige al menos un nivel por debajo de home.

Comodines y topics con $

Los topics que empiezan por $, como $SYS/broker/..., están reservados para información del broker. La especificación establece que un filtro que empieza con un comodín (# o +) no debe coincidir con ellos. Para leerlos tienes que suscribirte explícitamente, por ejemplo a $SYS/#. Así se evita que una suscripción de depuración a # se vea inundada de estadísticas del broker. En un broker de ConnectMQ, por ejemplo, el estado de conexión aparece en $SYS/broker/connection/{clientId}/state. Más información en los topics $SYS de MQTT.

Prueba los comodines MQTT en un broker real

El siguiente experimento usa el servidor MQTT público gratuito en broker.connectmq.com, pensado para pruebas y prototipos, no para datos de producción. Sustituye w1c5 por tu propia cadena aleatoria para ver solo tus propios mensajes, y mantén payloads inofensivos, porque cualquiera puede leer el área pública.

Primero, publica un pequeño árbol de topics retenidos para que los nuevos suscriptores lo reciban de inmediato:

B="-h broker.connectmq.com -p 1883 -i wc-pub-w1c5 -r -q 1"
R="connectmq/demo/w1c5"
mosquitto_pub $B -t "$R/home/kitchen/temperature" -m 21.5
mosquitto_pub $B -t "$R/home/kitchen/humidity"    -m 48
mosquitto_pub $B -t "$R/home/garage/temperature"  -m 14.2
mosquitto_pub $B -t "$R/home/garage/door"         -m closed
mosquitto_pub $B -t "$R/home"                     -m house-online
mosquitto_pub $B -t "$R/office/desk/temperature"  -m 23.0

Ahora ejecuta cada suscripción y compara la salida (pulsa Ctrl+C cuando aparezcan los mensajes retenidos):

S="-h broker.connectmq.com -p 1883 -i wc-sub-w1c5 -v"
R="connectmq/demo/w1c5"

# 1. Todas las temperaturas directamente bajo una habitación
mosquitto_sub $S -t "$R/home/+/temperature"

# 2. Todo lo de la cocina
mosquitto_sub $S -t "$R/home/kitchen/#"

# 3. Todo lo que hay bajo home, incluido el propio topic home
mosquitto_sub $S -t "$R/home/#"

# 4. Solo los hijos de home, no el propio home
mosquitto_sub $S -t "$R/home/+/#"

# 5. Temperaturas de cualquier edificio y habitación
mosquitto_sub $S -t "$R/+/+/temperature"

Resultados esperados: el filtro 1 devuelve dos topics; el filtro 2, dos; el filtro 3, cinco, incluido home; el filtro 4, cuatro; el filtro 5, tres, incluido el escritorio de la oficina.

Cuando termines, borra los mensajes retenidos publicando payloads vacíos con el flag retain:

for t in home/kitchen/temperature home/kitchen/humidity home/garage/temperature \
         home/garage/door home office/desk/temperature; do
  mosquitto_pub -h broker.connectmq.com -p 1883 -i wc-clean-w1c5 -r -n \
    -t "connectmq/demo/w1c5/$t"
done

Comodines en Python

import paho.mqtt.client as mqtt

ROOT = "connectmq/demo/w1c5"

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe([(f"{ROOT}/home/+/temperature", 0),
                      (f"{ROOT}/home/garage/#", 1)])

def on_message(client, userdata, msg):
    # Función auxiliar de coincidencia: comprueba a qué filtro pertenece un topic
    if mqtt.topic_matches_sub(f"{ROOT}/home/+/temperature", msg.topic):
        print("Temperature", msg.topic, msg.payload.decode())
    else:
        print("Garage", msg.topic, msg.payload.decode())

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="wc-py-w1c5")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()

Un mensaje que coincide con dos de tus filtros puede entregarse una vez o una vez por cada suscripción coincidente, según el broker; la especificación permite ambos comportamientos. Diseña los handlers para que lo toleren.

Cómo diseñar los topics para que los comodines trabajen a tu favor

Los comodines son tan útiles como el árbol de topics que tienen debajo. Como + y # operan sobre niveles completos, cada decisión sobre el orden de los niveles determina qué preguntas puede hacer un suscriptor con poco esfuerzo. Algunos patrones dan resultado una y otra vez.

Coloca cerca del principio la dimensión por la que más filtras. Si los operadores suelen mirar una planta cada vez, la planta debe ir pronto en el topic, para que acme/berlin/# la capture con un solo filtro. Si suelen comparar una medida en todas las plantas, deja la medida como último nivel para que funcione acme/+/+/+/temperature.

Mantén una profundidad coherente. Un filtro como acme/+/+/status asume que todos los dispositivos están exactamente dos niveles por debajo del namespace. Si algunos dispositivos están anidados un nivel más abajo, desaparecen en silencio de todos los dashboards construidos sobre ese filtro. Una profundidad coherente hace que + sea predecible.

Separa las categorías de mensajes en su propio nivel. Con ramas .../telemetry/..., .../state/... y .../cmd/..., un dispositivo puede suscribirse a acme/berlin/hall2/pump-3/cmd/# y recibir solo comandos, y un servicio de analítica puede tomar solo la telemetría sin ver nunca el tráfico de control.

No codifiques varios datos en un mismo nivel. Un nivel como berlin-hall2-pump3 no puede dividirse con comodines; berlin/hall2/pump3 sí.

Comodines y carga del broker

Comparar filtros con topics es el trabajo diario del broker, y los brokers bien diseñados gestionan los comodines con eficiencia. El coste que tú controlas es el fan-out: un filtro amplio implica más mensajes entregados a tu cliente, más ancho de banda y más procesamiento. En planes medidos o con cuotas, un filtro estrecho también es el filtro más barato. Cuando un backend necesite procesar una rama amplia, escálalo con grupos de consumidores en lugar de multiplicar suscripciones idénticas.

Errores habituales con los comodines MQTT

  1. Suscribirse a # en producción. Recibe todos los mensajes del broker (o del tenant), consume ancho de banda y puede alcanzar las cuotas del plan. Usa el filtro más estrecho que funcione.
  2. Esperar que + coincida con varios niveles. home/+/temperature no coincidirá con home/kitchen/sensor1/temperature. Usa un segundo + o reestructura los topics.
  3. Poner # en medio. home/#/temperature no es válido y la suscripción será rechazada.
  4. Olvidar que los filtros distinguen mayúsculas y minúsculas. home/+/Temperature no coincidirá con topics en minúsculas.
  5. Suscripciones solapadas. Los solapamientos pueden producir entregas duplicadas con distintos niveles de QoS.
  6. Alcanzar los límites. El broker público permite 10 suscripciones por cliente y 8 niveles de topic, así que es preferible un comodín bien situado a muchas suscripciones exactas.

Cuando una suscripción con comodín no recibe nada

Revisa estas comprobaciones en orden: confirma que el reason code del SUBACK indica éxito y no un filtro rechazado; imprime el topic exacto que usa el publicador y compáralo nivel a nivel con tu filtro, incluidas las mayúsculas y cualquier barra inicial; suscríbete temporalmente a la rama padre con # para ver qué se está publicando realmente; y, por último, comprueba que el publicador y el suscriptor están conectados al mismo broker y tenant. La mayoría de los informes de "los comodines no funcionan" acaban siendo un nivel de más o una letra mayúscula.

Los comodines también se combinan con las suscripciones compartidas de MQTT: un filtro como $share/workers/factory/+/telemetry/# reparte toda una rama entre un grupo de consumidores. Si quieres antes los fundamentos del protocolo, lee el tutorial de MQTT.

Explora topics con ConnectMQ

ConnectMQ es un broker MQTT gestionado que funciona como broker MQTT en la nube, con un explorador de topics en directo y un cliente MQTT integrado en el navegador dentro de su portal, para que puedas probar filtros con comodines contra tu árbol de topics real sin instalar herramientas. Crea un broker Serverless gratuito, sin tarjeta de crédito, y conecta tus dispositivos con YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID sobre TCP, TLS o WebSockets.

Preguntas frecuentes

¿Qué diferencia hay entre + y # en MQTT?

+ coincide con exactamente un nivel de topic y puede aparecer en cualquier posición de un filtro. # coincide con cero o más niveles y debe ser el último carácter del filtro.

¿Puedo publicar en un topic con un comodín?

No. Los comodines solo están permitidos en los filtros de suscripción. Publicar en un topic que contenga + o # es una violación del protocolo.

¿Coincide # con los topics $SYS?

No. Los filtros que empiezan con un comodín no coinciden con los topics que empiezan por $. Suscríbete explícitamente a $SYS/# para recibirlos.

¿Coincide home/# con el topic home?

Sí. El comodín multinivel también coincide con el nivel padre, así que home/# recibe los mensajes publicados en home además de todo lo que hay por debajo.

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.