Testes de brokers

Teste de latência MQTT: meça o tempo de ida e volta corretamente

Como fazer um teste de latência MQTT: meça o tempo de ida e volta com um único relógio, evite armadilhas de NTP, reporte p50/p99 e compare regiões de broker.

Atualizado · 8 min de leitura

Teste de latência MQTT: meça o tempo de ida e volta corretamente

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çãoDe → paraRelógio necessário
RTT em loopbackO cliente publica → o mesmo cliente recebe a própria mensagemUm relógio (fácil, confiável)
RTT com ecoCliente A → broker → cliente de eco B → broker → cliente AUm relógio em A
Latência em um sentidoPublisher no host A → subscriber no host BDois relógios sincronizados
Tempo de conexãoTCP + handshake TLS + CONNECT → CONNACKUm relógio
Linha de base da redeConexão TCP à porta do brokerUm 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 ou Date.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() ou process.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.

mqtt_rtt.py
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")
Executar
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 1000

As 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:

Linha de base TCP (inclui DNS na primeira chamada)
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

FatorEfeito no RTT
QoS 0 → QoS 1A entrega não espera o PUBACK, mas a persistência no broker e os limites de inflight podem adicionar tempo sob carga
QoS 2Handshake de quatro pacotes por salto; visivelmente mais lento, veja o benchmark QoS 0 vs 1 vs 2
TLSCusto do handshake na conexão; pequeno custo de criptografia por mensagem
WebSocketsFraming extra e, muitas vezes, um salto por proxy
Tamanho do payloadPayloads maiores demoram mais em links lentos
Carga do brokerAs filas crescem e a latência de cauda sobe primeiro
Callbacks do clienteUm 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:

  1. Rode a partir da mesma máquina e rede, de preferência uma VM na região onde ficam seus dispositivos ou seu backend.
  2. Use o mesmo QoS, payload, porta e configuração de TLS para todos os alvos.
  3. 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.
  4. Registre min, p50, p99 e lost de cada um, junto com a linha de base TCP, em uma única tabela.
  5. 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.