Se o seu cliente MQTT fica desconectando, a causa quase sempre é uma de cinco coisas: dois clients compartilhando um client ID, um timeout de keep alive, a rede derrubando silenciosamente conexões ociosas, um pacote que o broker não aceita ou um limite de plano ou de taxa. Este guia explica como reconhecer cada uma pelos sintomas e logs e percorre um fluxo de diagnóstico que leva você rapidamente à correção.
Comece pelo padrão das desconexões
Antes de mudar qualquer configuração, observe como as desconexões acontecem. O momento em que ocorrem diz muito:
| Padrão | Causa mais provável |
|---|---|
| Dois dispositivos se revezam desconectando a cada poucos segundos | Client ID duplicado |
| Desconexão em intervalo regular, aproximadamente 1,5 x keep alive | Keep alive não está sendo atendido |
| Desconexão após vários minutos ociosos, só em certas redes | Timeout de inatividade de NAT ou firewall |
| Desconexão logo após publicar uma mensagem específica | Pacote grande demais ou topic inválido |
| Desconexão quando o tráfego dispara | Limite de taxa ou cota |
| Desconexão imediatamente após cada conexão | Erro de autorização ou de assinatura |
Descubra também qual lado fecha a conexão. Com MQTT 5, um fechamento iniciado pelo broker vem com um pacote DISCONNECT e um reason code. Com MQTT 3.1.1, o broker simplesmente fecha o socket. Se puder, mude o client para MQTT 5 enquanto depura; o reason code muitas vezes responde à pergunta na hora. A referência de reason codes do MQTT lista todos eles.
Causa 1: client IDs duplicados
A especificação MQTT diz que, se um client se conecta com um client ID que já está conectado, o broker deve desconectar o client existente. Se os dois clients têm reconexão automática ativada, eles se derrubam mutuamente para sempre. No MQTT 5, o client que perde recebe o reason code 0x8E Session taken over.
Isso acontece com mais frequência do que você imagina:
- Firmware com client ID fixo no código gravado em vários dispositivos.
- Um serviço escalado para duas réplicas com a mesma configuração.
- Um desenvolvedor rodando o mesmo script em dois terminais.
- Uma ferramenta MQTT de desktop deixada aberta com o ID que seu dispositivo usa.
Solução: derive os client IDs de algo único, como endereço MAC, número de série ou hostname, e nunca compartilhe um ID entre processos. O guia de client ID MQTT aborda padrões de nomenclatura. Em um broker MQTT gerenciado que emite client IDs junto com as credenciais, use uma conexão por ID emitido.
Causa 2: timeouts de keep alive
O keep alive é uma promessa do client: "você terá notícias minhas pelo menos a cada N segundos". Se o broker não recebe nada por 1,5 vez esse intervalo, ele fecha a conexão, com o motivo 0x8D Keep Alive timeout no MQTT 5. O client envia PINGREQ quando não tem mais nada a enviar, mas só se o seu loop de rede estiver rodando.
Formas comuns de quebrar isso:
- Chamar uma função bloqueante longa, como
time.sleep(120)ou uma requisição HTTP lenta, na mesma thread que roda o loop MQTT. - No Arduino, esquecer de chamar
mqtt.loop()com frequência ou usardelay()por minutos seguidos. - Definir um keep alive muito curto, como 5 segundos, em um link celular de alta latência.
- Ciclos de deep sleep mais longos que o intervalo de keep alive, sem uma desconexão limpa.
Solução: rode o loop de rede em uma thread em segundo plano (loop_start() no Paho; o MQTT.js faz isso automaticamente), mantenha o trabalho bloqueante fora dessa thread e escolha um keep alive de 30 a 120 segundos para a maioria das implantações. O guia do intervalo de keep alive explica como ajustá-lo.
Causa 3: timeouts de inatividade de NAT e firewall
Redes móveis, roteadores domésticos e firewalls corporativos rastreiam conexões TCP e as derrubam silenciosamente após um período de inatividade. Nem o client nem o broker são avisados. O client só descobre o problema na próxima vez que tenta enviar algo, e o broker só quando o keep alive expira.
O sinal característico é que as desconexões acontecem na rede celular ou em um local específico, mas não na sua rede de desenvolvimento. Solução: defina o keep alive abaixo do timeout de inatividade da rede, para que o tráfego de PINGREQ mantenha o mapeamento ativo. Se uma operadora derruba conexões ociosas após alguns minutos, um keep alive de 60 segundos é um ponto de partida razoável.
Causa 4: pacotes grandes demais ou inválidos
Os brokers fecham a conexão quando recebem um pacote que não podem aceitar. Gatilhos típicos:
- Payload acima do tamanho máximo de mensagem do broker. Clients MQTT 5 veem
0x95 Packet too large; clients 3.1.1 simplesmente perdem a conexão. Veja o guia do limite de tamanho de mensagem MQTT. - Publicar em um topic que contém
+ou#. Wildcards só são válidos em assinaturas. - UTF-8 inválido em um nome de topic, produzido pela concatenação de strings com dados binários.
- Uso indevido de topic alias após uma reconexão no MQTT 5.
Solução: registre em log o topic e o tamanho do payload antes de cada publish e verifique se a desconexão vem depois de uma mensagem específica. A mesma mensagem grande demais muitas vezes é reenviada após a reconexão, o que cria um loop.
Causa 5: limites de taxa e cotas
Brokers compartilhados e brokers MQTT gerenciados se protegem com limites. Publicar mais rápido que o permitido, abrir mais conexões do que o seu plano permite ou exceder um número de assinaturas pode causar recusas ou desconexões. No MQTT 5, procure 0x96 Message rate too high e 0x97 Quota exceeded.
No servidor MQTT público (gratuito) em broker.connectmq.com, pensado para testes e protótipos, cada client está limitado a 10 mensagens por segundo, 10 assinaturas e 64 KB por mensagem. Brokers privados têm limites por plano; o plano Serverless gratuito, por exemplo, permite até 25 conexões e mensagens de 256 KB.
Um fluxo de diagnóstico que funciona
- Ative os logs do client. No Paho,
client.enable_logger(); nas ferramentas do Mosquitto,-d; no MQTT.js, definaDEBUG=mqttjs*. - Conecte-se com MQTT 5 e registre o reason code e a Reason String de cada DISCONNECT.
- Registre os timestamps das conexões e desconexões e compare o intervalo com o seu keep alive.
- Verifique o lado do broker. Observe o estado de conexão do client ID no broker e se outra conexão com o mesmo ID aparece no mesmo momento.
- Reproduza com um client mínimo. Rode o
mosquitto_subcom as mesmas credenciais, na mesma rede. Se ele continuar conectado, o problema está na sua aplicação. - Mude uma coisa de cada vez: client ID, keep alive, tamanho do payload, taxa de publicação.
Aqui está um client Python mínimo que registra tudo o que você precisa para os passos 1 a 3:
import time
import paho.mqtt.client as mqtt
started = time.time()
def ts():
return f"[{time.time() - started:7.1f}s]"
def on_connect(client, userdata, flags, reason_code, properties):
print(ts(), "CONNECT", reason_code, "session_present:", flags.session_present)
client.subscribe("connectmq/demo/5a3c/#", qos=1)
def on_disconnect(client, userdata, flags, reason_code, properties):
reason = getattr(properties, "ReasonString", "") if properties else ""
print(ts(), "DISCONNECT", reason_code, f"(0x{reason_code.value:02X})", reason)
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id="disconnect-debug-5a3c",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever(retry_first_connection=True)
Para acompanhar a visão que o broker tem de um client a partir de outro terminal, assine o topic de estado de conexão dele:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_MONITOR_CLIENT_ID \
-t '$SYS/broker/connection/YOUR_CLIENT_ID/state' -v
Coloque o topic entre aspas simples para que o shell não expanda $SYS. O guia de topics $SYS do MQTT explica o que mais você pode monitorar dessa forma. Como o broker vincula client IDs às credenciais, a conexão de monitoramento precisa do seu próprio client ID válido; o client MQTT integrado ao navegador no portal é uma alternativa fácil para uma olhada rápida.
Lógica de reconexão que não piora as coisas
A reconexão automática é essencial, mas uma implementação descuidada transforma um problema em vários:
- Use backoff exponencial com jitter para que uma frota não se reconecte toda ao mesmo tempo após uma queda.
- Não tente de novo em erros de autorização. Os códigos
0x86e0x87não se resolvem sozinhos, e repetidas falhas de login podem fazer seu IP ser bloqueado. - Assine novamente no
on_connect, e não uma única vez na inicialização, a menos que você dependa de uma sessão persistente. - Use um Last Will para que outros sistemas saibam quando um dispositivo cai inesperadamente. Veja MQTT Last Will and Testament.
Diagnostique desconexões com o ConnectMQ
O ConnectMQ oferece uma visibilidade do lado do broker que instalações auto-hospedadas muitas vezes não têm. O dashboard e a visualização de dispositivos do portal mostram quais clients estão conectados, o explorador de topics ao vivo mostra o que eles publicam e o topic $SYS/broker/connection/{clientId}/state permite que suas próprias ferramentas acompanhem as mudanças de conexão. Clients MQTT 5 recebem reason codes para violações de cota e de protocolo, então você vê por que uma desconexão aconteceu em vez de adivinhar. Para entender o que um broker MQTT faz durante a vida de uma conexão, veja nosso artigo explicativo, ou o tutorial de MQTT para o protocolo completo.
Tenha conexões estáveis com o ConnectMQ
Um broker MQTT gerenciado tira a disponibilidade do broker da sua lista de preocupações para que você se concentre no comportamento dos clients. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e teste sua lógica de reconexão contra limites reais antes de lançar. Compare as opções no nosso guia sobre o cenário de brokers MQTT na nuvem.
Perguntas frequentes
Por que meu cliente MQTT desconecta a cada poucos segundos?
A causa mais comum é outro client usando o mesmo client ID. Cada conexão derruba a outra, e as duas se reconectam automaticamente. Dê a cada client um ID único.
Que valor de keep alive devo usar?
Entre 30 e 120 segundos atende à maioria das implantações. Use um valor abaixo do timeout de inatividade da sua rede e garanta que o loop de rede do client rode continuamente para que o PINGREQ seja de fato enviado.
Como sei se foi o broker ou o client que fechou a conexão?
Conecte-se com MQTT 5. Se o broker a fechou, o client recebe um pacote DISCONNECT com um reason code; se o socket simplesmente caiu, o client informa um erro de rede sem reason code.
Uma mensagem grande pode causar desconexões?
Sim. Se um pacote excede o tamanho máximo do broker, o broker fecha a conexão, e se o client reenvia a mesma mensagem depois de reconectar, ele desconecta de novo. Compare os tamanhos de payload com o limite do seu broker.



