O MQTT garante a ordem das mensagens apenas dentro de um escopo restrito: mensagens de um publisher, em um topic, com um nível de QoS são entregues a cada subscriber na ordem em que o broker as recebeu. Entre topics, entre publishers e dentro de inscrições compartilhadas não há garantia de ordem — e mesmo dentro do escopo garantido, as retransmissões do QoS 1 podem gerar duplicatas atrasadas.
Este artigo explica exatamente o que a especificação promete, onde a ordem quebra na prática e traz dois scripts Python curtos que medem mensagens fora de ordem e duplicadas no seu próprio broker.
O que a especificação diz
Tanto o MQTT 3.1.1 quanto o MQTT 5 (seção 4.6, "Message ordering") definem cada topic como um Ordered Topic por padrão. Quando um broker encaminha mensagens em um topic ordenado, ele precisa enviar os pacotes PUBLISH aos subscribers "para o mesmo topic e QoS" na ordem em que os recebeu de cada cliente. Os clientes, por sua vez, precisam reenviar mensagens não confirmadas e enviar confirmações em ordem.
A especificação também aponta a pegadinha. Com QoS 1, um publisher que envia 1, 2, 3, 4 pode fazer um subscriber ver 1, 2, 3, 2, 3, 4: após uma reconexão, as cópias retransmitidas de 2 e 3 chegam depois das originais. A cópia final de cada mensagem está em ordem, mas uma duplicata pode aparecer depois da sua sucessora. Se nunca houver mais de uma mensagem em voo por vez, isso não acontece.
| Cenário | Ordem garantida? |
|---|---|
| Um publisher, um topic, mesmo QoS | Sim, por subscriber |
| Um publisher, vários topics | Não — o intercalamento entre topics é indefinido |
| Vários publishers, um topic | Só por publisher; o intercalamento entre eles é arbitrário |
| Mesmo publisher, QoS misturados em um topic | Não — a regra vale por QoS |
| QoS 1 após uma reconexão | Cópias finais em ordem; duplicatas podem chegar atrasadas |
| QoS 0 sob carga | A ordem do que chega é mantida, mas podem faltar mensagens |
| Grupo de inscrição compartilhada | Não — as mensagens são distribuídas entre os membros |
Níveis de QoS no MQTT explica os fluxos de confirmação por trás dessas regras.
Onde a ordem quebra na prática
- Várias mensagens em voo e reconexões. Clientes e brokers costumam permitir muitas mensagens QoS 1/2 não confirmadas ao mesmo tempo (no MQTT 5 o limite é negociado com o Receive Maximum). Se a conexão cai com várias em voo, o reenvio pode chegar depois de mensagens mais novas.
- Inscrições compartilhadas.
$share/group/topicdistribui as mensagens entre consumidores. Cada consumidor pode ver a sua parte em ordem, mas o processamento roda em paralelo, então o resultado geral fica fora de ordem. Veja inscrições compartilhadas. - Vários publishers. Dois gateways reportando o mesmo dispositivo produzem dois fluxos ordenados de forma independente. O broker pode intercalá-los de qualquer jeito.
- Bridges e clusters. Um bridge é só mais um cliente, então uma conexão de bridge mantém a ordem; vários caminhos entre brokers, não. Brokers em cluster geralmente preservam a ordem por publisher em um topic, mas confira a documentação do seu broker.
- O seu próprio consumidor. A causa mais comum de todas — as mensagens chegam em ordem e depois são processadas de forma concorrente (pools de threads, handlers async, filas de workers).
Teste 1: meça a ordem no seu broker
O publisher numera as mensagens por topic e por ID de publisher. O subscriber acompanha a maior sequência vista por (topic, publisher) e reporta tudo que volta para trás, cada duplicata e tudo que falta.
import os, sys, time
from collections import Counter, defaultdict
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
RUN = sys.argv[1] # mesmo id de execução do publisher
QOS = int(os.getenv("MQTT_QOS", "1"))
last = defaultdict(lambda: -1) # (topic, publisher) -> maior sequência
seen = defaultdict(set)
stats = Counter()
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(f"testmqtt/order/{RUN}/#", qos=QOS)
def on_message(client, userdata, msg):
pub_id, seq = msg.payload.decode().split(":")
key, seq = (msg.topic, pub_id), int(seq)
stats["received"] += 1
stats["dup_flag"] += msg.dup
if seq in seen[key]:
stats["duplicates"] += 1
return
seen[key].add(seq)
if seq < last[key]:
stats["out_of_order"] += 1
print(f"OUT OF ORDER {key}: {seq} after {last[key]}")
last[key] = max(last[key], seq)
def missing():
return sum(last[k] + 1 - len(seen[k]) for k in seen)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
if os.getenv("MQTT_USER"):
client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
client.on_connect, client.on_message = on_connect, on_message
client.connect(HOST, 1883)
client.loop_start()
try:
while True:
time.sleep(5)
print(dict(stats), "missing:", missing())
except KeyboardInterrupt:
print("FINAL", dict(stats), "missing:", missing())import os, sys
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
RUN, N = sys.argv[1], int(sys.argv[2]) if len(sys.argv) > 2 else 10000
PUB_ID = os.getenv("PUB_ID", "p1")
TOPICS = int(os.getenv("TOPICS", "4"))
QOS = int(os.getenv("MQTT_QOS", "1"))
pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
if os.getenv("MQTT_USER"):
pub.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
pub.max_inflight_messages_set(int(os.getenv("INFLIGHT", "20")))
pub.connect(HOST, 1883)
pub.loop_start()
seq, infos = [0] * TOPICS, []
for i in range(N):
t = i % TOPICS
infos.append(pub.publish(f"testmqtt/order/{RUN}/t{t}", f"{PUB_ID}:{seq[t]}", qos=QOS))
seq[t] += 1
for info in infos:
info.wait_for_publish(timeout=60)
pub.disconnect()
pub.loop_stop()
print(f"published {N} messages on {TOPICS} topics as {PUB_ID}")pip install "paho-mqtt>=2"
python order_sub.py run42 # terminal 1, inicie primeiro
python order_pub.py run42 10000 # terminal 2Em um broker saudável e uma rede estável, a linha de base deve terminar com out_of_order: 0, duplicates: 0 e missing: 0. Antes dos scripts, você pode acompanhar o fluxo ao vivo inscrevendo-se em testmqtt/order/# no cliente MQTT online enquanto o publisher roda.
Teste 2: provoque os casos extremos
Mude uma variável por vez e compare com a linha de base:
| Experimento | Como | O que você deve ver |
|---|---|---|
| Dois publishers | Rode order_pub.py duas vezes em paralelo com PUB_ID=p1 e PUB_ID=p2 | O fluxo de cada publisher continua ordenado; eles se intercalam |
| QoS 0 sob carga | MQTT_QOS=0, N maior, vários publishers | Possivelmente missing > 0, mas nada fora de ordem |
| Queda de rede | Bloqueie a porta 1883 do publisher por alguns segundos no meio da execução (veja QoS com queda de rede) | Duplicatas e contagem de dup_flag; reenvios atrasados possíveis com sessão persistente |
| Uma em voo | INFLIGHT=1 mais uma queda de rede | Duplicatas possíveis, mas nenhuma mensagem depois da sua sucessora |
| Inscrição compartilhada | Inscreva duas cópias de order_sub.py em $share/g/testmqtt/order/run42/# | Cada cópia vê lacunas — o outro membro recebeu essas mensagens |
Para o teste com inscrição compartilhada, troque a chamada de subscribe pelo filtro $share/...; você pode verificar se um filtro casa com seus topics no topic matcher. Duplicatas não são bug do broker — é o QoS 1 fazendo o seu trabalho; mensagens duplicadas mostra como tornar os consumidores idempotentes.
Não perca a ordem no seu próprio código
A maioria dos relatos de "o MQTT bagunçou a ordem das minhas mensagens" vem do consumidor. No Node.js, um handler de mensagens async não espera a mensagem anterior:
client.on("message", async (topic, payload) => {
await saveToDb(topic, payload); // a mensagem 2 pode terminar antes da 1
});Encadeie o trabalho por topic para manter a ordem por topic e, ainda assim, processar topics diferentes em paralelo:
const chains = new Map();
client.on("message", (topic, payload) => {
const prev = chains.get(topic) ?? Promise.resolve();
const next = prev.then(() => saveToDb(topic, payload)).catch(console.error);
chains.set(topic, next);
});O mesmo vale para pools de threads em Python ou Java e para filas de workers depois do broker: particione o trabalho por topic ou ID de dispositivo, nunca em round-robin.
Projetando para a ordem
- Coloque eventos que precisam ficar em ordem em um único topic, a partir de um único publisher — por exemplo
site/device42/events, e não um topic por tipo de evento. - Adicione um número de sequência ou timestamp de origem ao payload, para que os consumidores possam descartar atualizações antigas ("o último a escrever vence") ou reordenar janelas pequenas.
- Torne os handlers idempotentes, porque o QoS 1 vai entregar duplicatas.
- Trate inscrições compartilhadas como fora de ordem por definição; particione por dispositivo se a ordem importar.
- Use mensagens retidas para o "estado atual", em que só o valor mais recente conta e a ordem importa menos.
A garantia de ordem do MQTT é real, mas estreita: um publisher, um topic, um QoS. Dez minutos com um teste de números de sequência mostram como o seu broker e a sua rede se comportam, e as variantes mostram exatamente onde o seu design precisa assumir — normalmente com um número de sequência no payload e um consumidor que processa cada topic em ordem.
Perguntas frequentes
O MQTT garante a ordem das mensagens?
Em parte. A especificação exige que o broker entregue as mensagens de um mesmo publisher, no mesmo topic e com o mesmo QoS, na ordem em que as recebeu. Não há garantia de ordem entre topics, entre publishers diferentes nem entre membros de uma inscrição compartilhada.
Mensagens QoS 1 podem chegar fora de ordem?
Sim, de um jeito específico: depois de uma reconexão, uma mensagem QoS 1 retransmitida pode chegar depois de mensagens posteriores, por exemplo 1, 2, 3, 2, 3, 4. As cópias finais estão em ordem, mas duplicatas podem aparecer atrasadas. Manter uma única mensagem em voo por vez evita isso.
Como manter a ordem das mensagens MQTT entre vários topics?
O MQTT não faz isso por você. Coloque eventos relacionados em um único topic a partir de um único publisher, ou adicione um número de sequência ou timestamp ao payload e deixe o consumidor reordenar ou descartar mensagens antigas.


