Os wildcards MQTT permitem que uma única assinatura case com muitos topics: + corresponde a exatamente um nível de topic e # corresponde a qualquer número de níveis no final de um filtro. Neste guia você vai aprender as regras exatas de correspondência, os casos-limite que confundem as pessoas e um conjunto de comandos mosquitto_sub que você pode executar no servidor MQTT público para ver cada filtro em ação.
Topics vs Filtros de Topic
Os publicadores sempre publicam em um nome de topic concreto, como home/kitchen/temperature. Os assinantes assinam com um filtro de topic, que pode conter wildcards. O broker compara cada topic recebido com o filtro de cada assinatura, nível a nível, e entrega a mensagem a todas as correspondências. Esse roteamento é o núcleo do que um broker MQTT faz.
Os wildcards só são válidos em filtros. Publicar em um topic que contém + ou # é um erro de protocolo, e a maioria dos brokers desconecta o cliente.
O Wildcard de Nível Único: +
O + corresponde a exatamente um nível, seja qual for o conteúdo. Ele precisa ocupar um nível inteiro: home/+/temperature é válido, home/kit+/temperature não é.
| Filtro | Corresponde a | Não corresponde a |
|---|---|---|
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 |
Vale lembrar dois detalhes. Primeiro, o + também corresponde a um nível vazio, então home/+/temperature corresponde a home//temperature. Segundo, uma barra inicial cria um primeiro nível vazio, então +/home corresponde a /home. Ambos são bons motivos para seguir convenções de nomes de tópicos MQTT sensatas e evitar níveis vazios por completo.
Você pode usar vários + em um filtro. factory/+/+/temperature corresponde a toda temperatura dois níveis abaixo de factory, o que é ideal para dashboards que mostram uma medida em toda a frota, como as temperaturas de todas as linhas de uma planta em Joinville.
O Wildcard Multinível:
O # corresponde a qualquer número de níveis, inclusive zero, e precisa ser o último caractere do filtro, ocupando um nível inteiro.
| Filtro | Corresponde a | Não corresponde a |
|---|---|---|
home/# |
home, home/kitchen, home/kitchen/light/state |
office/kitchen |
home/kitchen/# |
home/kitchen, home/kitchen/temperature |
home/garage/temperature |
# |
Todo topic que não começa com $ |
$SYS/... |
home/+/light/# |
home/kitchen/light, home/kitchen/light/brightness |
home/light |
Filtros inválidos incluem home/#/temperature (# não está no fim), home# (não é um nível inteiro) e home/kitchen#.
Observe que home/# também corresponde ao próprio topic pai home. Se você quer apenas os filhos, use home/+/#, que exige pelo menos um nível abaixo de home.
Wildcards e Topics $
Topics que começam com $, como $SYS/broker/..., são reservados para informações do broker. A especificação diz que um filtro que começa com wildcard (# ou +) não deve corresponder a eles. Para lê-los, você precisa assiná-los explicitamente, por exemplo $SYS/#. Isso evita que uma assinatura # de depuração seja inundada com estatísticas do broker. Em um broker ConnectMQ, por exemplo, o estado das conexões aparece em $SYS/broker/connection/{clientId}/state. Mais detalhes em topics $SYS MQTT.
Teste Wildcards MQTT em um Broker Real
O experimento a seguir usa o servidor MQTT público (gratuito) em broker.connectmq.com, pensado para testes e protótipos, não para dados de produção. Substitua w1c5 pela sua própria string aleatória para ver apenas as suas mensagens e mantenha os payloads inofensivos, porque a área pública pode ser lida por qualquer pessoa.
Primeiro, publique uma pequena árvore de topics retidos para que novos assinantes a recebam imediatamente:
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
Agora execute cada assinatura e compare a saída (Ctrl+C depois que as mensagens retidas aparecerem):
S="-h broker.connectmq.com -p 1883 -i wc-sub-w1c5 -v"
R="connectmq/demo/w1c5"
# 1. Toda temperatura logo abaixo de um cômodo
mosquitto_sub $S -t "$R/home/+/temperature"
# 2. Tudo na cozinha
mosquitto_sub $S -t "$R/home/kitchen/#"
# 3. Tudo abaixo de home, incluindo o próprio topic home
mosquitto_sub $S -t "$R/home/#"
# 4. Somente os filhos de home, não o próprio home
mosquitto_sub $S -t "$R/home/+/#"
# 5. Temperaturas em qualquer prédio e cômodo
mosquitto_sub $S -t "$R/+/+/temperature"
Resultados esperados: o filtro 1 retorna dois topics; o filtro 2 retorna dois; o filtro 3 retorna cinco, incluindo home; o filtro 4 retorna quatro; o filtro 5 retorna três, incluindo a mesa do escritório.
Quando terminar, limpe as mensagens retidas publicando payloads vazios com a 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
Wildcards em 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):
# Auxiliar de correspondência: verifica a qual filtro um topic pertence
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()
Uma mensagem que corresponde a dois dos seus filtros pode ser entregue uma vez ou uma vez por assinatura correspondente, dependendo do broker; a especificação permite os dois comportamentos. Projete os handlers para tolerar isso.
Projetando Topics para que os Wildcards Trabalhem a Seu Favor
Os wildcards são tão úteis quanto a árvore de topics por baixo deles. Como + e # operam em níveis inteiros, cada decisão sobre a ordem dos níveis determina quais perguntas um assinante pode fazer com facilidade. Alguns padrões compensam repetidamente.
Coloque a dimensão pela qual você filtra com mais frequência perto do início. Se os operadores costumam olhar um site de cada vez, o site deve vir cedo no topic, para que acme/berlin/# o capture em um único filtro. Se eles costumam comparar uma medida entre todos os sites, mantenha a medida como último nível para que acme/+/+/+/temperature funcione.
Mantenha a profundidade consistente. Um filtro como acme/+/+/status pressupõe que todo dispositivo fica exatamente dois níveis abaixo do namespace. Se alguns dispositivos estiverem um nível mais fundo, eles somem silenciosamente de todo dashboard construído sobre esse filtro. Profundidade consistente torna o + previsível.
Separe as categorias de mensagem em um nível próprio. Com ramos .../telemetry/..., .../state/... e .../cmd/..., um dispositivo pode assinar acme/berlin/hall2/pump-3/cmd/# e receber apenas comandos, e um serviço de analytics pode pegar só a telemetria sem nunca ver tráfego de controle.
Não codifique vários fatos em um único nível. Um nível como berlin-hall2-pump3 não pode ser dividido por wildcards; berlin/hall2/pump3 pode.
Wildcards e Carga no Broker
Comparar filtros com topics é o trabalho diário do broker, e brokers bem projetados lidam com wildcards de forma eficiente. O custo que você controla é o fan-out: um filtro amplo significa mais mensagens entregues ao seu cliente, mais banda e mais processamento. Em planos tarifados ou com cota, um filtro restrito também é o filtro mais barato. Quando um backend precisa processar um ramo amplo, escale-o com grupos de consumidores em vez de multiplicar assinaturas idênticas.
Erros Comuns com Wildcards MQTT
- Assinar
#em produção. Recebe todas as mensagens do broker (ou tenant), consome banda e pode atingir as cotas do plano. Use o filtro mais restrito que funcione. - Esperar que
+corresponda a vários níveis.home/+/temperaturenão corresponde ahome/kitchen/sensor1/temperature. Use um segundo+ou reestruture os topics. - Colocar
#no meio.home/#/temperatureé inválido e a assinatura será rejeitada. - Esquecer que os filtros diferenciam maiúsculas de minúsculas.
home/+/Temperaturenão corresponde a topics em minúsculas. - Assinaturas sobrepostas. Sobreposições podem gerar entregas duplicadas em níveis de QoS diferentes.
- Atingir limites. O servidor MQTT público permite 10 assinaturas por cliente e 8 níveis de topic, então prefira um wildcard bem posicionado a muitas assinaturas exatas.
Quando uma Assinatura com Wildcard Não Recebe Nada
Siga estas verificações em ordem: confirme que o reason code do SUBACK indica sucesso, e não um filtro rejeitado; imprima o topic exato que o publicador usa e compare-o nível a nível com o seu filtro, incluindo maiúsculas e minúsculas e qualquer barra inicial; assine temporariamente o ramo pai com # para ver o que realmente está sendo publicado; e, por fim, verifique se publicador e assinante estão conectados ao mesmo broker e tenant. A maioria dos relatos de "os wildcards estão quebrados" acaba sendo um nível a mais ou uma letra maiúscula.
Os wildcards também se combinam com as shared subscriptions MQTT: um filtro como $share/workers/factory/+/telemetry/# distribui um ramo inteiro entre um grupo de consumidores. Se você quer primeiro o básico do protocolo, leia o tutorial de MQTT.
Explore Topics com a ConnectMQ
A ConnectMQ é um broker MQTT gerenciado, ou seja, um broker MQTT na nuvem com explorador de topics ao vivo e cliente MQTT no navegador dentro do portal, para que você teste filtros com wildcards na sua árvore de topics real sem instalar ferramentas. Crie um broker Serverless gratuito, sem cartão de crédito, e conecte seus dispositivos com YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID via TCP, TLS ou WebSockets.
Perguntas frequentes
Qual é a diferença entre + e # no MQTT?
O + corresponde a exatamente um nível de topic e pode aparecer em qualquer posição de um filtro. O # corresponde a zero ou mais níveis e precisa ser o último caractere do filtro.
Posso publicar em um topic com wildcard?
Não. Os wildcards só são permitidos em filtros de assinatura. Publicar em um topic que contém + ou # é uma violação do protocolo.
O # corresponde a topics $SYS?
Não. Filtros que começam com wildcard não correspondem a topics iniciados por $. Assine $SYS/# explicitamente para recebê-los.
O filtro home/# corresponde ao topic home?
Sim. O wildcard multinível também corresponde ao nível pai, então home/# recebe as mensagens publicadas em home e tudo o que está abaixo dele.



