A latência MQTT é o tempo entre um publicador enviar uma mensagem e um assinante recebê-la e, na maioria dos sistemas IoT, ela é dominada por fatores externos ao protocolo. Este guia decompõe cada componente do tempo de ida e volta, explica quais ajustes realmente importam e oferece um pequeno script Python para medir a latência a partir da sua própria rede contra um broker ao vivo.
O que "latência" significa no MQTT
As pessoas usam a palavra para várias medições diferentes, então seja preciso antes de otimizar:
- Latência de ida (one-way): do publicador ao assinante. Difícil de medir com precisão porque os dois relógios estão em máquinas diferentes.
- Tempo de ida e volta (RTT): um cliente publica em um tópico que ele mesmo assina e mede quanto tempo a mensagem leva para voltar. Um único relógio, então o número é confiável. É isso que o script abaixo mede.
- Latência de conexão: tempo desde a abertura do socket até o recebimento do CONNACK. Relevante para dispositivos que acordam, conectam, publicam e voltam a dormir.
- Latência de confirmação: tempo do PUBLISH até o PUBACK com QoS 1, que indica quando o broker assumiu a responsabilidade pela mensagem.
Para dispositivos que mantêm uma conexão aberta (o padrão normal do MQTT), o tempo de ida e volta é o número isolado mais útil.
Os componentes da latência MQTT
Uma mensagem que vai de um sensor a um painel passa por várias etapas. Cada uma acrescenta atraso:
| Etapa | O que acontece | Influência típica | Principal alavanca |
|---|---|---|---|
| Pilha do cliente | A biblioteca enfileira, serializa e escreve no socket | Pequena, mas grande em microcontroladores ocupados | Loop não bloqueante, evitar chamadas longas de delay() |
| Rede de última milha | Acesso Wi-Fi, celular, satélite | Muitas vezes a parte maior e mais variável | Link melhor, sinal celular, gateway cabeado |
| Caminho na internet | Roteamento até a região do broker | Cresce com a distância física | Região do broker próxima aos dispositivos |
| TLS | Criptografia por pacote, handshake por conexão | O handshake custa idas e voltas extras uma vez; o custo por pacote é pequeno | Manter conexões de longa duração |
| Handshake de QoS | PUBACK ou fluxo de 4 etapas da QoS 2 | Não atrasa muito a entrega, mas limita a vazão | Escolher a QoS por tipo de dado |
| Processamento no broker | Autenticação, correspondência de tópicos, fan-out | Geralmente pequeno em um broker saudável | Evitar fan-outs enormes e tópicos quentes |
| Lado do assinante | O mesmo caminho ao contrário, mais o processamento da aplicação | Espelha o lado do publicador | Callbacks leves |
Distância de rede
A luz na fibra é rápida, mas não instantânea, e as rotas reais não são linhas retas. Um dispositivo em uma fazenda no Mato Grosso ou em uma mineradora no Pará conversando com um broker em outro continente sempre terá uma linha de base maior do que um que conversa com um broker na própria região. Nenhuma configuração de protocolo elimina isso; só a localização do broker resolve.
Overhead do TLS
O handshake TLS acrescenta idas e voltas quando a conexão é aberta. Depois disso, a criptografia simétrica custa muito pouco em CPUs modernas e apenas um pouco em chips como o ESP32. Como os clientes MQTT normalmente ficam conectados por horas ou dias, o TLS afeta principalmente a latência de conexão, e não a latência das mensagens em regime contínuo. Por isso "o TLS é lento" raramente é um bom motivo para usar a porta 1883; veja portas MQTT explicadas para os prós e contras.
Nível de QoS
Esta é a parte mais mal compreendida. Com QoS 0, o broker encaminha imediatamente. Com QoS 1, o broker também encaminha imediatamente ao receber, e o PUBACK volta em paralelo, então a latência de entrega é parecida com a da QoS 0 em um link saudável. A QoS 2 adiciona um handshake de quatro etapas; dependendo da implementação do broker, o encaminhamento pode ocorrer após a primeira ou a segunda etapa. O efeito maior de uma QoS mais alta está na vazão e no que acontece durante perda de pacotes: retransmissões aumentam a latência quando a rede se comporta mal. O guia de níveis de QoS do MQTT cobre os fluxos em detalhes.
Nagle, buffers e o loop do cliente
Algumas pilhas atrasam escritas pequenas para combiná-las. Muitas bibliotecas MQTT desativam o algoritmo de Nagle, mas pilhas embarcadas e sockets personalizados podem não fazer isso. Em microcontroladores, um loop principal que chama client.loop() apenas uma vez por segundo acrescenta até um segundo de latência às mensagens recebidas, por mais rápida que seja a rede.
Keep alive e reconexões
O keep alive não afeta a latência em uma conexão saudável, mas determina a rapidez com que uma conexão morta é detectada. Um link derrubado silenciosamente com keep alive de 300 segundos pode significar minutos até o cliente se reconectar, e todas as mensagens nessa janela chegam atrasadas.
Meça você mesmo a latência MQTT
O script abaixo se conecta ao servidor MQTT público em broker.connectmq.com, assina um tópico único, publica nele mensagens com timestamp e calcula estatísticas de ida e volta. Ele fica abaixo do limite do servidor MQTT público de 10 mensagens por segundo por cliente, enviando duas por segundo.
# mqtt_rtt.py - mede a latência de ida e volta do MQTT
# pip install "paho-mqtt>=2.0"
import json
import statistics
import sys
import threading
import time
import uuid
import paho.mqtt.client as mqtt
HOST = "broker.connectmq.com"
USE_TLS = "--tls" in sys.argv
PORT = 8883 if USE_TLS else 1883
QOS = 1
SAMPLES = 50
INTERVAL = 0.5 # segundos entre publicações (2 msg/s)
run_id = uuid.uuid4().hex[:8]
topic = f"connectmq/demo/{run_id}/rtt"
rtts = []
subscribed = threading.Event()
done = threading.Event()
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected to {HOST}:{PORT} -> {reason_code}")
client.subscribe(topic, qos=QOS)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
subscribed.set()
def on_message(client, userdata, msg):
received = time.perf_counter()
data = json.loads(msg.payload)
rtts.append((received - data["t"]) * 1000.0)
if len(rtts) >= SAMPLES:
done.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"rtt-{run_id}")
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
if USE_TLS:
client.tls_set() # usa o repositório de CAs do sistema
t0 = time.perf_counter()
client.connect(HOST, PORT, keepalive=60)
client.loop_start()
if not subscribed.wait(10):
sys.exit("Subscribe timed out")
print(f"Connect + subscribe took {(time.perf_counter() - t0) * 1000:.1f} ms")
for seq in range(SAMPLES):
payload = json.dumps({"seq": seq, "t": time.perf_counter()})
client.publish(topic, payload, qos=QOS)
time.sleep(INTERVAL)
done.wait(10)
client.loop_stop()
client.disconnect()
if not rtts:
sys.exit("No messages received")
rtts.sort()
p95 = rtts[int(len(rtts) * 0.95) - 1]
print(f"samples={len(rtts)} lost={SAMPLES - len(rtts)}")
print(f"min={rtts[0]:.1f} ms median={statistics.median(rtts):.1f} ms "
f"p95={p95:.1f} ms max={rtts[-1]:.1f} ms")
Execute duas vezes e compare:
python mqtt_rtt.py # TCP sem criptografia na 1883
python mqtt_rtt.py --tls # TLS na 8883
Interpretando os resultados
- O mínimo se aproxima do caminho no melhor caso: rede mais broker sem nada atrapalhando. É o seu piso.
- A mediana é o que os usuários costumam experimentar.
- O p95 e o máximo revelam o jitter causado por retentativas de Wi-Fi, agendamento da rede celular ou links congestionados. Para malhas de controle e alarmes, eles importam mais do que a mediana.
- Connect + subscribe mostra o custo do handshake TLS. Espere um valor visivelmente maior com
--tls, enquanto a mediana por mensagem fica próxima à da execução em TCP sem criptografia. - Amostras perdidas com QoS 1 geralmente significam que o script terminou antes de as mensagens atrasadas chegarem, ou que a conexão caiu. Verifique desconexões no console.
Rode o teste de onde seus dispositivos realmente estão (a rede da planta, um modem celular, um roteador doméstico), e não apenas do escritório. A diferença costuma ser a descoberta mais valiosa.
Formas práticas de reduzir a latência MQTT
- Mantenha as conexões abertas. Conecte uma vez e publique muitas vezes. Reconectar a cada mensagem paga o handshake TCP e TLS toda vez.
- Escolha uma região do broker próxima aos dispositivos. A distância define o piso.
- Escolha a QoS por tipo de dado. Telemetria de alta frequência, em que a próxima amostra substitui a anterior, pode usar QoS 0; comandos e alarmes merecem QoS 1.
- Mantenha os payloads compactos. Documentos JSON grandes demoram mais para serem enviados em links lentos. Considere codificações binárias para dados de alta frequência e topic aliases do MQTT 5 para encurtar nomes de tópicos repetidos.
- Atenda o loop do cliente com frequência. No Arduino ou no ESP32, chame
loop()a cada iteração e evite delays bloqueantes. - Ajuste o keep alive para detectar links mortos rapidamente (30 a 60 segundos é comum) sem desperdiçar banda.
- Evite saltos desnecessários. Cada bridge, gateway ou relay Node-RED no caminho acrescenta seu próprio processamento e enfileiramento.
- Faça callbacks de assinante rápidos. Passe o trabalho pesado para uma thread ou fila de workers, para que o loop de rede não fique bloqueado.
Solução de problemas de latência alta ou com picos
| Sintoma | Causa provável | O que verificar |
|---|---|---|
| RTT mínimo alto | Distância até o broker ou rede de acesso lenta | Ping e traceroute até o host do broker |
| Mediana boa, p95 péssimo | Retentativas de Wi-Fi, agendamento celular | Teste em um link cabeado para comparar |
| Latência cresce com o tempo | Assinante não dá conta, mensagens se acumulam | Duração dos callbacks e contagem de mensagens inflight |
| Lacunas periódicas de vários segundos | Código bloqueante no loop do cliente | sleep longo ou E/S síncrona nos callbacks |
| Picos apenas na reconexão | Handshake TLS mais retomada de sessão | Estabilidade da conexão e keep alive |
| Limitação no servidor MQTT público | Limite de taxa de 10 msg/s por cliente | Reduza a taxa de publicação ou use um broker privado |
Da medição à produção
O servidor MQTT público é compartilhado e serve apenas para testes, então trate os números dele como indicativos do seu caminho de rede, e não como benchmark. Para tráfego de produção, um broker MQTT gerenciado como o ConnectMQ oferece um endpoint privado e autenticado nas portas 8883 e 8084, sem os limites de taxa públicos; os detalhes dos planos estão no guia de broker MQTT na nuvem. Para repetir o teste contra o seu próprio broker ConnectMQ, adicione client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD"), defina o client ID como YOUR_CLIENT_ID e execute com --tls. Para um contexto mais amplo do protocolo, veja o tutorial de MQTT e como o MQTT funciona.
Comece a usar o ConnectMQ
Crie um broker MQTT gerenciado no plano Serverless (Free) em app.connectmq.com, sem cartão de crédito, e aponte o script de latência para ele com duas linhas. O cliente no navegador e o explorador de tópicos ao vivo do ConnectMQ ajudam você a ver exatamente quando cada mensagem chega.
Perguntas frequentes
Qual é uma boa latência MQTT?
Depende da distância e da rede de acesso. Compare seu RTT mínimo com um ping de rede simples até o host do broker; se o MQTT acrescenta só um pouco a mais, o broker não é o seu gargalo.
A QoS 1 deixa o MQTT mais lento que a QoS 0?
Em um link saudável, a latência de entrega é parecida, porque o broker encaminha antes de o PUBACK ser concluído. A QoS 1 acrescenta overhead principalmente quando pacotes se perdem e precisam ser reenviados.
O TLS aumenta a latência MQTT?
O TLS acrescenta tempo de handshake na conexão, mas em uma conexão de longa duração o overhead por mensagem é pequeno. Manter as conexões abertas é a melhor forma de minimizar o impacto.
Por que medir a latência de ida e volta em vez da latência de ida?
O tempo de ida e volta usa um único relógio, então evita erros causados por relógios não sincronizados nas máquinas do publicador e do assinante.



