No MQTT, o tópico é a única coisa que liga publicadores e assinantes. Não há filas nem exchanges para configurar — o broker simplesmente compara o tópico de cada mensagem com todos os filtros de assinatura. Isso faz do design de tópicos a espinha dorsal do seu sistema: ele determina o que os clientes podem assinar, como as permissões são expressas e quão fácil será adicionar novos consumidores depois.
Fundamentos dos tópicos
Um nome de tópico é uma string UTF-8 formada por níveis separados por uma barra:
building-a/floor-3/room-12/temperature
vehicles/truck-0042/gps
acme/devices/sensor-17/status- Tópicos diferenciam maiúsculas de minúsculas.
Home/Kitchenehome/kitchensão tópicos diferentes. - Não é preciso registrar nada. Um tópico passa a existir no momento em que alguém publica nele.
- Cada caractere conta. Uma barra inicial cria um primeiro nível vazio, então
/home/kitchennão é o mesmo quehome/kitchen. Espaços são permitidos, mas causam confusão sem fim. - Tamanho. Nomes de tópicos devem ter no mínimo um caractere e no máximo 65.535 bytes, e não podem conter o caractere nulo.
Curingas: + e #
Os assinantes usam filtros de tópico, que podem conter dois curingas. Os publicadores sempre usam nomes de tópico concretos — curingas em um PUBLISH são uma violação do protocolo.
Curinga de nível único: +
+ corresponde a exatamente um nível. Ele precisa ocupar um nível inteiro (sensor+ é inválido), mas pode aparecer em qualquer posição e mais de uma vez.
| Filtro | Corresponde a | Não corresponde a |
|---|---|---|
home/+/temperature | home/kitchen/temperature | home/kitchen/oven/temperature |
+/+/status | acme/sensor-17/status | acme/status |
home/+ | home/kitchen | home, home/kitchen/light |
Curinga multinível: #
# corresponde a qualquer número de níveis — inclusive zero — e precisa ser o último caractere do filtro, sozinho em seu nível. home/# corresponde a home/kitchen, home/kitchen/light/state e também ao próprio nível pai home. Filtros como home/#/light ou home# são inválidos.
Um # sozinho assina todos os tópicos do broker (exceto os tópicos $). Isso é útil para depurar o seu próprio broker privado, mas em um broker público você será inundado pelo tráfego de outras pessoas.
$SYS e outros tópicos $
Tópicos que começam com $ são reservados ao broker. O mais comum é $SYS/, onde muitos brokers (Mosquitto, EMQX e outros) publicam estatísticas como clientes conectados, taxas de mensagens e uptime. Os tópicos exatos dependem do broker — a especificação só reserva o prefixo.
Curingas no primeiro nível não correspondem a tópicos $: # e +/monitor nunca vão entregar mensagens de $SYS/.... Assine explicitamente:
mosquitto_sub -h test.mosquitto.org -t '$SYS/broker/clients/connected' -vO MQTT 5 adiciona outro prefixo reservado: as assinaturas compartilhadas usam $share/<group>/<filter>. Todos os assinantes do mesmo grupo dividem o fluxo, e cada mensagem vai para apenas um membro — uma forma simples de balancear a carga entre consumidores. Veja MQTT 5 vs 3.1.1 para outras novidades do MQTT 5, como os topic aliases.
Projetando uma hierarquia de tópicos
Uma boa hierarquia vai do geral para o específico, de modo que curingas no final selecionem grupos com significado. Um padrão comum é:
<org-or-app>/<site>/<device-type>/<device-id>/<measurement-or-channel>
acme/berlin/thermostat/th-0193/temperature
acme/berlin/thermostat/th-0193/setpoint/set
acme/berlin/thermostat/th-0193/statusCom esse layout:
acme/berlin/#entrega a um dashboard do site tudo o que há em Berlim.acme/+/thermostat/+/temperatureentrega a um serviço de analytics todas as leituras de termostatos.acme/berlin/thermostat/th-0193/#entrega o fluxo completo de um único dispositivo.
Boas práticas
- Não comece com uma barra. Ela adiciona um nível vazio sem utilidade.
- Use minúsculas, ASCII e hífens. Evite espaços e letras maiúsculas misturadas; eles produzem divergências sutis.
- Coloque o ID do dispositivo ou do cliente no tópico. Isso facilita o controle de acesso por cliente (muitos brokers conseguem substituir o client ID ou o nome de usuário em padrões de ACL).
- Separe dados, comandos e estado. Por exemplo,
.../temperaturepara telemetria,.../setpoint/setpara comandos e.../statuspara estado retido (retained) ou presença via Last Will. - Mantenha os tópicos curtos, mas descritivos. O tópico é enviado com cada mensagem (a menos que você use topic aliases do MQTT 5), então nomes muito longos custam banda em links limitados.
- Não codifique dados no tópico. Valores pertencem ao payload; um tópico por valor gera uma quantidade ilimitada de tópicos e muitas mensagens retidas.
- Versione seu esquema se ele puder mudar. Um prefixo como
v1/permite que dispositivos antigos e novos coexistam durante uma migração. - Evite assinaturas amplas em produção. Assinar
#em um serviço de backend faz de toda mensagem do broker um problema dele.
Erros comuns com tópicos
- Esperar que
a/+corresponda aa. Não corresponde —+exige que um nível esteja presente. Usea/#se quiser incluir também o nível pai. - Barras no final.
home/kitchen/tem um último nível vazio e é um tópico diferente dehome/kitchen. - Assinaturas sobrepostas. Se um cliente assina tanto
home/#quantohome/+/temperature, a especificação permite que o broker entregue uma única cópia ou uma cópia por assinatura correspondente, então alguns brokers vão lhe entregar duplicatas. Mantenha as assinaturas disjuntas sempre que possível. - Misturar formatos de ID. Se alguns dispositivos publicam como
Sensor_17e outros comosensor-17, todo filtro e toda regra de ACL precisam tratar os dois casos. Normalize os IDs no provisionamento. - Tratar tópicos como banco de dados. Brokers são otimizados para roteamento, não para consultas. Se você precisa de histórico, faça um consumidor assinar os tópicos e gravar as mensagens em um banco de dados.
Tópicos e controle de acesso
Os brokers autorizam operações de publicação e assinatura por tópico, então sua hierarquia funciona também como seu modelo de permissões. Se cada dispositivo escreve apenas sob acme/<site>/<type>/<device-id>/, você pode conceder exatamente essa subárvore e nada mais. Se os tópicos forem planos ou inconsistentes, escrever regras restritas se torna impossível e as equipes acabam liberando # para todo mundo.
Experimente você mesmo
Abra o cliente MQTT online, assine testmqtt/demo/+/temperature e publique em testmqtt/demo/kitchen/temperature e testmqtt/demo/kitchen/oven/temperature. Só a primeira mensagem chega — exatamente como a regra do + prevê.
Perguntas frequentes
Qual a diferença entre + e # no MQTT?
O curinga + corresponde a exatamente um nível de tópico e pode aparecer em qualquer posição do filtro. O curinga # corresponde a qualquer número de níveis, inclusive zero, e precisa ser o último caractere do filtro.
Posso publicar em um tópico que contém um curinga?
Não. Curingas só são válidos em filtros de assinatura. Um PUBLISH com + ou # no nome do tópico é um erro de protocolo, e o broker vai encerrar a conexão.
Assinar # inclui os tópicos $SYS?
Não. Filtros que começam com um curinga não correspondem a tópicos iniciados por $. Para receber as estatísticas do broker, você precisa assiná-las explicitamente, por exemplo com $SYS/#.