Um teste de latência MQTT mede quanto tempo uma mensagem leva para ir de um publisher, passando pelo broker, até um subscriber. A forma confiável de fazer isso é medir o tempo de ida e volta (RTT) em um único relógio: o mesmo processo publica uma mensagem, recebe-a de volta do broker e subtrai duas leituras de um timer monotônico. Qualquer coisa que compare timestamps de duas máquinas mede tanto a diferença entre os relógios quanto a rede.
Este guia traz um script de latência pronto para copiar, explica as armadilhas de relógio que estragam a maioria das medições, mostra como reportar percentis e como comparar brokers ou regiões de forma justa.
O que "latência MQTT" realmente significa
Vários números diferentes são chamados de latência. Decida qual você precisa antes de escrever qualquer código:
| Medição | De → para | Relógio necessário |
|---|---|---|
| RTT em loopback | O cliente publica → o mesmo cliente recebe a própria mensagem | Um relógio (fácil, confiável) |
| RTT com eco | Cliente A → broker → cliente de eco B → broker → cliente A | Um relógio em A |
| Latência em um sentido | Publisher no host A → subscriber no host B | Dois relógios sincronizados |
| Tempo de conexão | TCP + handshake TLS + CONNECT → CONNACK | Um relógio |
| Linha de base da rede | Conexão TCP à porta do broker | Um relógio |
Para planejamento de capacidade e comparação de brokers, o RTT em loopback costuma bastar. Ele inclui o trabalho de roteamento do broker nas duas direções e duas travessias de rede, e não precisa de nada além de um único cliente.
Armadilhas de relógio
A maioria dos números ruins de latência vem do relógio, não do broker:
- O relógio de parede dá saltos.
time.time()em Python ouDate.now()em JavaScript seguem o relógio do sistema, que o NTP pode ajustar a qualquer momento. Use um relógio monotônico para intervalos:time.perf_counter()em Python,performance.now()ouprocess.hrtime.bigint()em Node.js. - Duas máquinas nunca concordam exatamente. Mesmo com NTP, é comum os hosts divergirem em um milissegundo ou mais, e muito mais em dispositivos sem uma boa fonte de tempo. Uma medição em um sentido herda esse erro diretamente — você pode até obter latências negativas.
- RTT / 2 é uma estimativa. Os caminhos de upload e download podem ser diferentes (rede celular, ADSL, uplinks congestionados). Reporte o RTT como RTT e só divida por dois quando disser isso.
- Resolução do timer em dispositivos pequenos.
millis()em um microcontrolador tem resolução de 1 ms; faça a média de muitas amostras em vez de confiar em uma única leitura.
Um script de RTT em loopback
Este script (paho-mqtt 2.x) se inscreve em um topic único, publica números de sequência um de cada vez e espera cada mensagem voltar antes de enviar a próxima. Esse design de malha fechada mede a latência de um caminho ocioso, sem efeitos de fila.
import os, statistics, sys, threading, time, uuid
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
N = int(sys.argv[1]) if len(sys.argv) > 1 else 500
QOS = int(os.getenv("MQTT_QOS", "1"))
WARMUP = 20
TOPIC = f"testmqtt/latency/{uuid.uuid4().hex[:8]}"
ready, got = threading.Event(), threading.Event()
state = {"expected": -1, "t0": 0.0}
samples, lost = [], 0
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(TOPIC, qos=QOS)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
ready.set()
def on_message(client, userdata, msg):
if int(msg.payload) == state["expected"]:
rtt = (time.perf_counter() - state["t0"]) * 1000
if state["expected"] >= WARMUP:
samples.append(rtt)
got.set()
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_subscribe, client.on_message = on_connect, on_subscribe, on_message
client.connect(HOST, PORT, keepalive=30)
client.loop_start()
ready.wait(10)
for i in range(N + WARMUP):
got.clear()
state["expected"], state["t0"] = i, time.perf_counter()
client.publish(TOPIC, str(i), qos=QOS)
if not got.wait(5):
lost += 1
time.sleep(0.02) # mantém o caminho ocioso entre as sondas
client.loop_stop()
q = statistics.quantiles(samples, n=100)
print(f"{HOST}:{PORT} QoS {QOS} samples {len(samples)} lost {lost}")
print(f"min {min(samples):.1f} p50 {q[49]:.1f} p95 {q[94]:.1f} "
f"p99 {q[98]:.1f} max {max(samples):.1f} ms")pip install "paho-mqtt>=2"
python mqtt_rtt.py 1000
MQTT_PORT=8883 python mqtt_rtt.py 1000 # requer client.tls_set() para TLS
MQTT_QOS=0 python mqtt_rtt.py 1000As primeiras WARMUP mensagens são descartadas porque pagam por caches frios e pelo aquecimento da conexão. Se você precisar testar com TLS, adicione client.tls_set() antes de connect() e use a porta 8883 — o handshake acontece uma vez, então afeta o tempo de conexão, não o RTT por mensagem.
Antes de rodar o script, vale uma verificação rápida no cliente MQTT online: inscreva-se em testmqtt/latency/#, publique uma mensagem e confirme que ela volta. Se isso funcionar, o script também vai funcionar.
Separe a rede do broker
Um RTT de 40 ms não diz nada até você saber quanto disso é rede. Meça o tempo de conexão TCP simples à porta do broker como linha de base:
python3 -c "import socket,time
for _ in range(5):
t=time.perf_counter(); socket.create_connection(('broker.connectmq.com',1883)).close()
print(f'{(time.perf_counter()-t)*1000:.1f} ms')"Uma conexão TCP corresponde a uma ida e volta na rede. Se o seu RTT MQTT em loopback com QoS 0 estiver próximo dessa linha de base, o broker adiciona quase nada e o restante da latência é distância. Se for muito maior, investigue o broker, o cliente ou o caminho — solução de problemas de latência alta mostra as causas mais comuns.
A luz na fibra percorre cerca de 200 km por milissegundo, então um broker a 2.000 km custa pelo menos ~20 ms de RTT antes de qualquer software rodar. Nenhum ajuste de broker vence a física; escolha uma região próxima dos seus dispositivos.
Reporte percentis, não médias
Distribuições de latência têm caudas longas. Uma média de 12 ms pode esconder um p99 de 300 ms, e é o p99 que causa timeouts nos dispositivos e faz os dashboards travarem. Sempre reporte pelo menos:
- min — o piso definido pela rede; deve ser estável entre execuções.
- p50 — a experiência típica.
- p95 / p99 — a cauda; compare esses valores entre configurações.
- max e lost — outliers isolados e timeouts são sinais, não ruído.
Colete amostras suficientes para o percentil reportado. Com 100 amostras, o p99 é na prática uma única mensagem; procure ter pelo menos 1.000 amostras antes de citar um p99.
O que muda a latência
| Fator | Efeito no RTT |
|---|---|
| QoS 0 → QoS 1 | A entrega não espera o PUBACK, mas a persistência no broker e os limites de inflight podem adicionar tempo sob carga |
| QoS 2 | Handshake de quatro pacotes por salto; visivelmente mais lento, veja o benchmark QoS 0 vs 1 vs 2 |
| TLS | Custo do handshake na conexão; pequeno custo de criptografia por mensagem |
| WebSockets | Framing extra e, muitas vezes, um salto por proxy |
| Tamanho do payload | Payloads maiores demoram mais em links lentos |
| Carga do broker | As filas crescem e a latência de cauda sobe primeiro |
| Callbacks do cliente | Um on_message lento atrasa todas as mensagens seguintes |
Para ver como a latência se comporta sob carga, rode este script enquanto um gerador de carga envia tráfego — o método está em teste de carga de broker MQTT. O RTT ocioso é o seu melhor caso; o RTT sob carga é o que você usa no planejamento.
Comparando brokers e regiões de forma justa
Uma comparação só é justa quando tudo, exceto o broker, é idêntico:
- Rode a partir da mesma máquina e rede, de preferência uma VM na região onde ficam seus dispositivos ou seu backend.
- Use o mesmo QoS, payload, porta e configuração de TLS para todos os alvos.
- Rode os alvos intercalados (A, B, A, B) em vez de todo o A e depois todo o B, para que um soluço temporário da rede afete os dois.
- Registre min, p50, p99 e lost de cada um, junto com a linha de base TCP, em uma única tabela.
- Repita em outro horário do dia antes de tirar conclusões.
Para decisões multirregião, crie uma VM pequena em cada região onde você tem usuários e rode o mesmo script contra cada endpoint de broker. A matriz resultante de p50/p99 por (região do cliente, região do broker) é o dado mais honesto que você vai conseguir.
Checklist do teste de latência
- Use um relógio monotônico e um único processo para o RTT.
- Descarte as mensagens de aquecimento; meça o tempo de conexão separadamente.
- Colete mais de 1.000 amostras e reporte min, p50, p95, p99, max e lost.
- Registre uma linha de base TCP ao lado de cada resultado MQTT.
- Teste ocioso e sob carga, a partir da rede que seus dispositivos realmente usam.
- Mantenha o script no seu repositório e rode-o de novo após mudanças no broker ou na rede.
Confira a ida e volta no navegador
Um teste de latência MQTT confiável é principalmente uma questão de disciplina: um único relógio, amostras suficientes, percentis em vez de médias e uma linha de base que mostre quanto do número pertence à rede. Acerte isso e um script de 15 minutos responde perguntas que os benchmarks de fornecedores nunca vão responder.
Perguntas frequentes
Como medir a latência MQTT sem relógios sincronizados?
Meça o tempo de ida e volta em um único relógio: um cliente publica em um topic no qual também está inscrito (ou para um cliente de eco que republica), e o mesmo processo registra o envio e o recebimento com um relógio monotônico.
O tempo de ida e volta MQTT é o dobro da latência em um sentido?
Só aproximadamente. RTT / 2 supõe que as duas direções são simétricas, o que muitas vezes não é verdade em redes móveis ou assimétricas. Para números reais em um sentido, você precisa de relógios sincronizados bem abaixo da latência medida.
Por que minha primeira mensagem MQTT é muito mais lenta que as outras?
As primeiras mensagens pagam pela consulta DNS, pelo aquecimento de TCP e TLS, pela criação da inscrição e por caches frios. Descarte um lote de aquecimento antes de registrar amostras e meça o tempo de conexão separadamente.


