Fundamentos de MQTT

Como Apagar uma Mensagem Retida no MQTT (CLI, Python e Client Web)

Uma retained message obsoleta continua recebendo cada novo assinante com dados antigos. Veja como apagar uma mensagem retida no MQTT com um publish retido vazio, pela linha de comando, Python, JavaScript e um client no navegador.

Ilustração da árvore de topics de um broker em que um valor obsoleto é limpo, mostrando como apagar mensagem retida MQTT
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Para apagar mensagem retida MQTT você não precisa de uma ferramenta de administração do broker: basta publicar um payload de comprimento zero com a flag retain ativada no mesmo topic, e o broker descarta o valor armazenado. Este guia mostra essa técnica pela linha de comando, Python, JavaScript e um client no navegador, além de como encontrar e limpar em massa, com segurança, topics retidos obsoletos.

Por que as retained messages ficam presas

Uma retained message é a última mensagem que o broker armazena para um topic, para que qualquer novo assinante receba imediatamente o valor atual. É exatamente o que você quer para o status de um dispositivo, um documento de configuração ou a leitura mais recente de um sensor. Se o recurso é novidade para você, o guia de retained messages no MQTT explica a mecânica em detalhes.

O problema começa quando o valor armazenado deixa de ser verdadeiro. Situações típicas:

  • Um dispositivo foi descomissionado, por exemplo um sensor retirado de uma estação de saneamento, mas seu último status online continua aparecendo nos dashboards.
  • Um publish de teste com a flag retain, feito durante o desenvolvimento, agora polui uma árvore de topics de produção.
  • Um topic de configuração foi renomeado, e o antigo ainda entrega configurações desatualizadas a novos clients.
  • Um bug de firmware publicou JSON malformado com retain, e todo assinante que se reconecta trava ao processá-lo.

Como o broker reenvia o valor retido a cada nova assinatura, o problema nunca some sozinho. Você precisa substituir o valor armazenado ou removê-lo.

Como apagar uma mensagem retida no MQTT

A especificação MQTT define a regra em uma frase: um pacote PUBLISH com a flag RETAIN igual a 1 e payload de comprimento zero remove qualquer retained message daquele topic, e o broker não armazena uma nova. A mensagem vazia também é encaminhada aos assinantes atuais, então seus consumidores devem tolerar um payload vazio em vez de tentar interpretá-lo como JSON.

Alguns detalhes importam na prática:

  1. O topic precisa coincidir exatamente. As retained messages são armazenadas por nome de topic. Publicar uma mensagem retida vazia em sensors/# não faz nada, porque wildcards não são permitidos em nomes de topic nos quais se publica.
  2. O payload precisa estar realmente vazio. Um único espaço, um objeto JSON vazio {} ou a string null são payloads reais e serão retidos como o novo valor.
  3. O QoS não importa muito. QoS 0 basta para uma limpeza, mas QoS 1 dá a você uma confirmação de que o broker a recebeu.
  4. Você precisa de permissão de publish naquele topic. Se suas credenciais não podem publicar ali, a exclusão é rejeitada ou descartada silenciosamente, dependendo do broker e da versão do protocolo.

Limpando retained messages com mosquitto_pub

As ferramentas de linha de comando do Mosquitto são a forma mais rápida de limpar um único topic. A flag -r ativa o retain e -n envia uma mensagem nula (de comprimento zero):

# Inspecione o que está retido atualmente no topic
mosquitto_sub -h broker.connectmq.com -p 1883 -i inspect-7f3a \
  -t "connectmq/demo/7f3a/device42/status" -v -C 1 -W 5

# Apague a retained message
mosquitto_pub -h broker.connectmq.com -p 1883 -i cleanup-7f3a \
  -t "connectmq/demo/7f3a/device42/status" -r -n

# Confirme: agora isto deve estourar o tempo sem imprimir nada
mosquitto_sub -h broker.connectmq.com -p 1883 -i verify-7f3a \
  -t "connectmq/demo/7f3a/device42/status" -v -C 1 -W 5

A combinação -C 1 -W 5 diz ao mosquitto_sub para sair após uma mensagem ou cinco segundos, o que o transforma em uma sonda rápida de "há algo retido aqui?". Os exemplos usam o servidor MQTT público (gratuito, para testes e protótipos, não para dados de produção) com um prefixo único; substitua 7f3a pelo seu próprio ID aleatório.

Em um broker MQTT gerenciado privado, adicione credenciais e TLS:

mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID \
  -t "plant1/line2/press/status" -r -n -q 1

Limpeza em massa com --remove-retained

O mosquitto_sub tem uma opção útil para subárvores inteiras. Com --remove-retained, cada retained message recebida é limpa com a publicação de uma mensagem retida vazia de volta naquele topic. Combine-a com --retained-only para que ela só toque nos valores retidos e ignore o tráfego ao vivo:

mosquitto_sub -h broker.connectmq.com -p 1883 -i purge-7f3a \
  -t "connectmq/demo/7f3a/#" --retained-only --remove-retained -v -W 5

Rode primeiro o comando de inspeção, leia a lista com atenção e só então faça a limpeza. Um wildcard como # em uma árvore compartilhada pode apagar muito mais do que você pretendia.

Apagando uma mensagem retida com Python

Quando a limpeza faz parte de um script de descomissionamento, Python com paho-mqtt 2.x é conveniente. Passar payload=None produz um payload de comprimento zero:

import time
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/7f3a/device42/status"

def on_connect(client, userdata, flags, reason_code, properties):
    print("Connected:", reason_code)
    info = client.publish(TOPIC, payload=None, qos=1, retain=True)
    info.wait_for_publish(timeout=5)
    print("Retained message cleared on", TOPIC)
    client.disconnect()

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="cleanup-py-7f3a")
client.on_connect = on_connect
client.connect(BROKER, 1883, keepalive=30)
client.loop_forever()

Encontrando e limpando uma subárvore inteira

Esta versão assina com um wildcard, coleta todos os topics que entregam uma retained message durante uma janela curta e depois os limpa um a um. Ela limita a taxa de publicação, o que importa no servidor MQTT público, onde cada client está limitado a 10 mensagens por segundo.

import time
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
FILTER = "connectmq/demo/7f3a/#"
found = set()

def on_message(client, userdata, msg):
    if msg.retain and msg.payload:
        found.add(msg.topic)

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(FILTER, qos=1)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="purge-py-7f3a")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
time.sleep(3)  # coleta as retained messages

print(f"Found {len(found)} retained topics")
for topic in sorted(found):
    print("Clearing", topic)
    client.publish(topic, payload=None, qos=1, retain=True).wait_for_publish(5)
    time.sleep(0.15)  # fica abaixo do limite de taxa por client

client.loop_stop()
client.disconnect()

Repare na verificação msg.retain. Os brokers ativam a flag retain nas mensagens entregues por causa de uma nova assinatura, então mensagens ao vivo que por acaso cheguem durante a janela não são confundidas com valores armazenados.

Apagando uma mensagem retida via JavaScript ou navegador

Com o MQTT.js vale a mesma regra: uma string vazia ou um buffer vazio com retain: true.

const mqtt = require("mqtt");

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  clientId: "cleanup-js-7f3a",
  protocolVersion: 5,
});

client.on("connect", () => {
  client.publish(
    "connectmq/demo/7f3a/device42/status",
    "",
    { qos: 1, retain: true },
    (err) => {
      console.log(err ? `Failed: ${err.message}` : "Retained message cleared");
      client.end();
    }
  );
});

Se você prefere não escrever código algum, o client MQTT integrado ao navegador no portal ConnectMQ também funciona: conecte-se ao seu broker, informe o topic exato, deixe o campo de payload vazio, ative o retain e publique. O explorador de topics ao vivo é uma boa forma de ver quais topics ainda guardam um valor antes e depois da limpeza.

Solução de problemas quando uma mensagem retida não some

Sintoma Causa provável Solução
O valor antigo reaparece segundos depois Um dispositivo ou serviço ainda o publica com retain Encontre o publicador, corrija sua flag retain e limpe de novo
Nada muda após o publish vazio Topic diferente (maiúsculas, barra final, nível extra) Copie o topic exato da saída de uma assinatura com -v
Assinantes travam após a limpeza Os consumidores tentam interpretar o payload vazio Ignore mensagens de comprimento zero no handler de mensagens
O valor é substituído, não removido O payload era um espaço, {} ou null Use -n ou payload=None, não uma string de preenchimento
A limpeza funciona no teste, mas não em produção As credenciais não têm permissão de publish naquele topic Use a conta dona da árvore de topics

A primeira linha é, de longe, a mais comum. Um valor retido que "volta" quase sempre está sendo republicado por alguma coisa, muitas vezes um script de bridge, um fluxo do Node-RED ou um dispositivo que publica seu status com retain a cada reconexão.

Evite dados retidos obsoletos desde o início

Apagar retained messages é uma tarefa de limpeza; projetá-las bem reduz a frequência com que você precisa fazer isso.

  • Use o message expiry do MQTT 5. Defina um Message Expiry Interval nos publishes retidos para que o broker os descarte após um período. O guia do message expiry interval aborda valores sensatos.
  • Combine status com Last Will. Um status online retido deve vir acompanhado de uma mensagem de Last Will offline retida, para que o valor armazenado continue correto quando um dispositivo cair.
  • Mantenha os topics retidos restritos. Retenha estado, não fluxos. Telemetria de alta frequência raramente precisa de retain, e menos topics retidos significam menos coisa para limpar.
  • Não trate retained messages como banco de dados. Elas guardam o valor mais recente por conveniência, não um histórico durável. Armazene o que você precisa manter em um banco de dados de verdade, alimentado por um assinante.
  • Documente quais topics são retidos. Uma lista curta nas suas convenções de nomenclatura de topics evita surpresas quando alguém adiciona uma assinatura com wildcard.

O comportamento completo da flag retain está especificado no padrão OASIS MQTT 5.0, e o tutorial de MQTT, mais amplo, coloca as retained messages em contexto com QoS, sessões e wills.

Gerencie topics retidos no ConnectMQ

O ConnectMQ suporta retained messages tanto no MQTT 3.1.1 quanto no 5.0, então todas as técnicas acima funcionam sem alterações. Se você está indo além dos experimentos, um broker MQTT gerenciado oferece credenciais privadas, TLS na porta 8883 e um portal com client integrado ao navegador e explorador de topics ao vivo para esse tipo de manutenção. Você pode ler mais sobre como rodar um broker MQTT na nuvem sem operar servidores você mesmo.

Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e em poucos minutos você terá username, password e client ID.

Perguntas frequentes

Como apago uma mensagem retida no MQTT?

Publique uma mensagem com payload de comprimento zero e a flag retain ativada exatamente no mesmo topic. Com o Mosquitto, isso é mosquitto_pub -t <topic> -r -n, e o broker deixa de armazenar um valor retido para aquele topic.

Posso apagar todas as mensagens retidas com um wildcard?

Não com um único publish, porque wildcards não são permitidos em nomes de topic. Assine com um filtro wildcard para descobrir os topics retidos e depois limpe cada um, ou use mosquitto_sub --retained-only --remove-retained.

Enviar um objeto JSON vazio apaga a mensagem retida?

Não. {} é um payload de dois bytes, então o broker o retém como o novo valor. Só um payload realmente vazio remove a retained message.

Os assinantes recebem a mensagem vazia?

Sim, os assinantes atuais recebem a mensagem de comprimento zero como qualquer outro publish. Garanta que seus consumidores ignorem payloads vazios em vez de falhar ao interpretá-los.

Artigos relacionados

Por que Seu Cliente MQTT Fica Desconectando (e Como Corrigir)

Loops de reconexão estão entre os problemas mais comuns do MQTT. Conheça as cinco causas habituais, de client IDs duplicados a timeouts de keep alive e pacotes grandes demais, e siga um fluxo de diagnóstico que identifica a sua.

9 min de leitura

Quem Inventou o MQTT? A História dos Oleodutos à IoT

O MQTT foi inventado em 1999 por Andy Stanford-Clark, da IBM, e Arlen Nipper, da Arcom, para monitorar oleodutos via satélite. Acompanhe sua trajetória até o open source, a OASIS, a ISO e os brokers MQTT gerenciados na nuvem de hoje.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.