Mensagens offline MQTT são as publicações que um broker guarda para um assinante temporariamente desconectado e entrega quando ele volta, como um gateway de fazenda que perde o sinal celular por alguns minutos. Este artigo explica exatamente quais mensagens se qualificam, quais limites se aplicam em brokers reais e conduz você por um teste reproduzível para que você veja o enfileiramento e a entrega acontecendo.
As Quatro Condições para Mensagens Offline MQTT
O enfileiramento não é automático. Um broker só enfileira uma mensagem para um cliente offline quando todas estas condições são verdadeiras:
| Condição | O que verificar |
|---|---|
| O cliente tem uma sessão persistente | MQTT 3.1.1: cleanSession = false. MQTT 5: Clean Start = false e Session Expiry Interval > 0 |
| A sessão não expirou | A expiração do MQTT 5 ainda não passou e os limites do broker não foram excedidos |
| O cliente já assinava um filtro de topic correspondente | A assinatura precisa existir antes de ele ficar offline |
| O QoS efetivo é 1 ou 2 | O QoS de entrega é o menor entre o QoS do publish e o da assinatura |
Se qualquer condição falhar, a mensagem simplesmente não é entregue àquele cliente. Não há erro para o publicador, porque o MQTT desacopla publicadores de assinantes por design. É por isso que bugs do tipo "meu dispositivo perdeu um comando" são tão comuns: o publicador recebe um PUBACK de sucesso do broker, mas o broker não tinha motivo para guardar a mensagem para aquele assinante específico.
E o QoS 0?
QoS 0 é "no máximo uma vez". A especificação permite que um broker enfileire mensagens QoS 0 para sessões offline, mas não exige isso, e a maioria dos brokers não o faz. Trate o QoS 0 como exclusivamente ao vivo. As diferenças entre os níveis são abordadas em detalhes em níveis de QoS MQTT explicados.
E as Retained Messages?
As retained messages são um mecanismo separado. O broker guarda a última mensagem retida por topic e a envia a qualquer um que assine, tenha ou não uma sessão. Elas resolvem "qual é o valor atual?", não "o que eu perdi?". Um cliente que se reconecta com sessão limpa ainda recebe o último valor retido ao assinar, mas nenhuma das atualizações intermediárias. Um bom design costuma usar as duas coisas: retained messages para estado e mensagens QoS 1 enfileiradas para eventos e comandos.
Limites Que se Aplicam às Filas Offline
Todo broker impõe limites às filas, porque, caso contrário, um cliente desconectado que nunca volta consumiria memória para sempre.
- Tempo de vida da sessão. No MQTT 5, o session expiry interval decide por quanto tempo a fila sobrevive. Quando ele expira, as mensagens enfileiradas são descartadas. No servidor MQTT público broker.connectmq.com, o session expiry é limitado a 30 minutos. A escolha dos valores é abordada em session expiry interval MQTT.
- Profundidade da fila. Os brokers limitam o número de mensagens ou de bytes enfileirados por sessão. Quando o limite é atingido, novas mensagens são descartadas ou as antigas são removidas, dependendo do broker.
- Expiração da mensagem. Um publicador MQTT 5 pode definir um Message Expiry Interval. Se uma mensagem ficar na fila por mais tempo do que isso, o broker a descarta em vez de entregar dados obsoletos.
- Tamanho da mensagem. Os limites de tamanho de mensagem do plano e do broker também valem para mensagens enfileiradas (64 KB no servidor MQTT público, 256 KB no plano Serverless Free da ConnectMQ, 1 MB no plano Serverless pago).
- Reinícios do broker. O local onde as filas ficam depende do broker. A ConnectMQ mantém as sessões e suas filas em memória: elas sobrevivem a desconexões do cliente pelo tempo de vida de sessão concedido, mas não são mantidas após um reinício do broker. Os clientes devem detectar uma sessão perdida (Session Present = 0 no CONNACK), assinar novamente e contar com retained messages ou com uma ressincronização na camada de aplicação para recuperar o estado.
Teste Reproduzível: Veja a Entrega Offline Acontecer
Este teste leva cerca de cinco minutos com as ferramentas de linha de comando do mosquitto. Substitua q5t9 pela sua própria string aleatória e mantenha os payloads inofensivos: o servidor MQTT público pode ser lido por qualquer pessoa e serve para testes e protótipos, não para dados de produção.
- Crie a sessão. Conecte um assinante com ID fixo, Clean Start false, session expiry de 15 minutos e QoS 1:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i offline-sub-q5t9 -c -x 900 -q 1 -v \
-t 'connectmq/demo/q5t9/cmd/#'
Fique offline. Pressione Ctrl+C.
Publique uma mistura de níveis de QoS enquanto o assinante está ausente:
H="-h broker.connectmq.com -p 1883 -V mqttv5 -i offline-pub-q5t9"
mosquitto_pub $H -q 0 -t 'connectmq/demo/q5t9/cmd/a' -m 'qos0 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/b' -m 'qos1 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/c' -m 'short-lived' \
-D publish message-expiry-interval 5
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/other' -m 'not subscribed'
- Espere pelo menos 10 segundos e execute de novo o comando da etapa 1.
Você deve ver apenas connectmq/demo/q5t9/cmd/b qos1 message. Veja por que cada uma das outras não aparece:
cmd/aera QoS 0, então não foi enfileirada.cmd/ctinha um message expiry de 5 segundos e foi descartada na fila.othernão correspondia ao filtro da assinatura.
- Repita sem
-cna etapa 1. Nada é entregue na reconexão, porque a sessão foi descartada quando a conexão fechou.
O Mesmo Padrão em Python
Um dispositivo que se reconecta e processa tudo o que perdeu:
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
TOPIC = "connectmq/demo/q5t9/cmd/#"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("Connect failed:", reason_code)
return
print("Connected. Session present:", flags.session_present)
if not flags.session_present:
# Sessão nova ou perdida: assine e recupere o estado de outra forma
client.subscribe(TOPIC, qos=1)
def on_message(client, userdata, msg):
print(f"Processing {msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="offline-py-q5t9",
protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.reconnect_delay_set(min_delay=1, max_delay=60)
props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 900
client.connect("broker.connectmq.com", 1883, keepalive=60,
clean_start=False, properties=props)
client.loop_forever()
Projetando para Clientes Offline
O enfileiramento ajuda em interrupções curtas, mas não deve ser a sua única ferramenta de confiabilidade. Um design robusto combina vários mecanismos:
- Use QoS 1 para comandos e eventos e QoS 0 para telemetria de alta frequência em que a próxima leitura substitui a anterior.
- Mantenha o session expiry realista. Longo o bastante para cobrir as interrupções típicas e curto o bastante para que dispositivos mortos não acumulem filas.
- Defina message expiry em comandos sensíveis ao tempo, para que um dispositivo que volta depois de uma hora não execute uma instrução desatualizada.
- Publique o estado como retained messages, para que qualquer cliente possa se ressincronizar instantaneamente sem depender de uma fila.
- Torne os handlers idempotentes. O QoS 1 pode entregar duplicatas após reconexões; inclua um ID de mensagem ou número de sequência no payload.
- Trate o Session Present = 0 como um evento normal, não como erro.
- Espere uma rajada na reconexão. Clientes que estavam offline podem receber muitas mensagens de uma vez; o Receive Maximum do MQTT 5 limita quantas mensagens não confirmadas chegam em paralelo.
Para entender as sessões em si, leia sessões persistentes MQTT e a flag clean session. Se você está começando agora, o tutorial de MQTT cobre primeiro o básico de publish/subscribe.
Resolvendo Problemas de Mensagens Offline Ausentes
- Nada chega após a reconexão: confirme que o client ID é idêntico, que o Clean Start é false e, no MQTT 5, que o session expiry é diferente de zero.
- Algumas mensagens chegam e outras não: compare o QoS de cada publish e da assinatura e verifique a expiração das mensagens.
- Mensagens chegam duas vezes: é esperado com QoS 1 após uma confirmação interrompida; elimine as duplicatas na aplicação.
- O Session Present é 0 inesperadamente: a sessão expirou, o client ID mudou ou o broker reiniciou.
Enfileire Mensagens Offline com a ConnectMQ
A ConnectMQ é um broker MQTT gerenciado que suporta sessões persistentes, session expiry e message expiry no MQTT 5.0, além de clientes MQTT 3.1.1. O plano Serverless Free suporta QoS até 1, que é tudo o que o enfileiramento offline exige, e o QoS 2 está disponível nos planos pagos. Crie o seu broker gratuito sem cartão de crédito, conecte-se com YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID e execute o teste acima no seu próprio endpoint privado. Se estiver comparando provedores, veja o que avaliar em um broker MQTT na nuvem.
Perguntas frequentes
O MQTT armazena mensagens quando o assinante está offline?
Somente se o assinante tiver uma sessão persistente, já assinava um topic correspondente e o QoS efetivo for 1 ou 2. Caso contrário, a mensagem não é guardada para aquele cliente.
Por que minhas mensagens QoS 0 se perdem enquanto o dispositivo está offline?
Mensagens QoS 0 normalmente não são enfileiradas para sessões offline; a especificação torna isso opcional. Publique e assine com QoS 1 se a mensagem precisar sobreviver a uma desconexão.
Por quanto tempo um broker guarda mensagens offline?
Até a sessão expirar, um limite de fila ser atingido, a expiração da própria mensagem passar ou o broker reiniciar, caso as sessões fiquem em memória. Na ConnectMQ as sessões vivem em memória, e o servidor MQTT público limita o session expiry a 30 minutos.
As retained messages substituem o enfileiramento offline?
Não. Uma retained message entrega a um novo assinante o último valor de um topic, enquanto o enfileiramento offline entrega cada mensagem QoS 1 ou 2 que o cliente perdeu. A maioria dos sistemas usa as duas.



