La latencia MQTT es el tiempo que transcurre entre que un publicador envía un mensaje y un suscriptor lo recibe, y en la mayoría de sistemas IoT está dominada por factores ajenos al propio protocolo. Esta guía desglosa cada componente del tiempo de ida y vuelta, explica qué ajustes importan de verdad y te da un pequeño script en Python para medir la latencia desde tu propia red contra un broker real.
Qué significa "latencia" en MQTT
La palabra se usa para varias mediciones distintas, así que sé preciso antes de optimizar:
- Latencia en un sentido: del publicador al suscriptor. Es difícil de medir con exactitud porque los dos relojes están en máquinas distintas.
- Tiempo de ida y vuelta (RTT): un cliente publica en un topic al que también está suscrito y mide cuánto tarda en volver el mensaje. Un solo reloj, así que el número es fiable. Es lo que mide el script de más abajo.
- Latencia de conexión: tiempo desde que se abre el socket hasta que se recibe el CONNACK. Relevante para dispositivos que se despiertan, se conectan, publican y vuelven a dormir.
- Latencia de confirmación: tiempo desde el PUBLISH hasta el PUBACK con QoS 1, que te indica cuándo el broker ha asumido la responsabilidad del mensaje.
Para dispositivos que mantienen una conexión abierta (el patrón normal en MQTT), el tiempo de ida y vuelta es el número individual más útil.
Los componentes de la latencia MQTT
Un mensaje que va de un sensor a un dashboard pasa por varias etapas. Cada una añade retardo:
| Etapa | Qué ocurre | Influencia típica | Palanca principal |
|---|---|---|---|
| Pila del cliente | La biblioteca encola, serializa y escribe en el socket | Pequeña, pero grande en microcontroladores ocupados | Bucle no bloqueante, evitar llamadas largas a delay() |
| Red de última milla | Acceso Wi-Fi, celular, satélite | A menudo la parte mayor y más variable | Mejor enlace, señal celular, gateway cableado |
| Ruta por internet | Enrutamiento hasta la región del broker | Crece con la distancia física | Región del broker cercana a los dispositivos |
| TLS | Cifrado por paquete, handshake por conexión | El handshake cuesta viajes de ida y vuelta extra una vez; el coste por paquete es pequeño | Mantener conexiones de larga duración |
| Handshake de QoS | PUBACK o flujo de 4 pasos de QoS 2 | No retrasa mucho la entrega, pero limita el throughput | Elegir QoS según el tipo de dato |
| Procesamiento del broker | Autenticación, coincidencia de topics, fan-out | Normalmente pequeño en un broker sano | Evitar fan-outs enormes y topics calientes |
| Lado del suscriptor | El mismo camino a la inversa, más el procesamiento de la aplicación | Refleja el lado del publicador | Callbacks ligeros |
Distancia de red
La luz en la fibra es rápida, pero no instantánea, y las rutas reales no son líneas rectas. Un dispositivo en Estambul que habla con un broker en otro continente siempre tendrá una base más alta que uno que habla con un broker de su propia región. Ningún ajuste del protocolo puede eliminar esto; solo la ubicación del broker.
Sobrecarga de TLS
El handshake TLS añade viajes de ida y vuelta al abrir la conexión. Después, el cifrado simétrico cuesta muy poco en CPUs modernas y solo una cantidad moderada en chips como el ESP32. Como los clientes MQTT suelen permanecer conectados durante horas o días, TLS afecta sobre todo a la latencia de conexión, no a la latencia de los mensajes en régimen estable. Por eso "TLS es lento" rara vez es un buen motivo para usar el puerto 1883; consulta puertos MQTT explicados para ver las ventajas e inconvenientes.
Nivel de QoS
Es la parte peor entendida. Con QoS 0, el broker reenvía de inmediato. Con QoS 1, el broker también reenvía de inmediato al recibir el mensaje, y el PUBACK vuelve en paralelo, así que en un enlace sano la latencia de entrega es similar a la de QoS 0. QoS 2 añade un handshake de cuatro pasos; según la implementación del broker, el reenvío puede producirse tras el primer o el segundo paso. El mayor efecto de un QoS más alto está en el throughput y en lo que ocurre cuando se pierden paquetes: las retransmisiones añaden latencia cuando la red se comporta mal. La guía de niveles de QoS de MQTT cubre los flujos en detalle.
Nagle, buffers y el bucle del cliente
Algunas pilas retrasan las escrituras pequeñas para combinarlas. Muchas bibliotecas MQTT desactivan el algoritmo de Nagle, pero puede que las pilas embebidas y los sockets personalizados no lo hagan. En microcontroladores, un bucle principal que llama a client.loop() solo una vez por segundo añade hasta un segundo de latencia a los mensajes entrantes, por rápida que sea la red.
Keep-alive y reconexiones
El keep-alive no afecta a la latencia en una conexión sana, pero decide lo rápido que se detecta una conexión muerta. Un enlace caído en silencio con un keep-alive de 300 segundos puede suponer minutos hasta que el cliente se reconecta, y todos los mensajes de ese intervalo llegan tarde.
Mide tú mismo la latencia MQTT
El script siguiente se conecta al servidor MQTT público gratuito broker.connectmq.com, pensado para pruebas y prototipos, se suscribe a un topic único, publica en él mensajes con marca de tiempo y calcula estadísticas de ida y vuelta. Se mantiene por debajo del límite del servidor público de 10 mensajes por segundo por cliente enviando dos por segundo.
# mqtt_rtt.py - mide la latencia de ida y vuelta de 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 publicaciones (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 el almacén de CA del 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")
Ejecútalo dos veces y compara:
python mqtt_rtt.py # TCP sin cifrar en 1883
python mqtt_rtt.py --tls # TLS en 8883
Cómo leer los resultados
- El mínimo se aproxima al camino en el mejor caso: red más broker sin nada en medio. Es tu suelo.
- La mediana es lo que experimentan normalmente los usuarios.
- El p95 y el máximo revelan el jitter causado por reintentos de Wi-Fi, la planificación de la red celular o enlaces congestionados. Para bucles de control y alarmas, importan más que la mediana.
- Connect + subscribe muestra el coste del handshake TLS. Es de esperar que sea notablemente mayor con
--tls, mientras que la mediana por mensaje se mantiene cerca de la de la ejecución sobre TCP sin cifrar. - Las muestras perdidas con QoS 1 suelen significar que el script terminó antes de que llegaran los mensajes rezagados o que la conexión se cayó. Revisa la consola en busca de desconexiones.
Ejecuta la prueba desde donde viven realmente tus dispositivos (la red de planta, un módem celular, un router doméstico), no solo desde tu oficina. La diferencia suele ser el hallazgo más valioso.
Formas prácticas de reducir la latencia MQTT
- Mantén las conexiones abiertas. Conéctate una vez y publica muchas. Reconectar por cada mensaje paga cada vez el handshake TCP y TLS.
- Elige una región del broker cercana a tus dispositivos. La distancia marca el suelo.
- Elige el QoS según el tipo de dato. La telemetría de alta frecuencia, en la que la siguiente muestra sustituye a la anterior, puede usar QoS 0; los comandos y las alarmas merecen QoS 1.
- Mantén los payloads compactos. Los documentos JSON grandes tardan más en enviarse por enlaces lentos. Valora codificaciones binarias para datos de alta frecuencia y los topic aliases de MQTT 5 para acortar nombres de topic repetidos.
- Atiende el bucle del cliente a menudo. En Arduino o ESP32, llama a
loop()en cada iteración y evita los retardos bloqueantes. - Ajusta el keep-alive para detectar rápido los enlaces muertos (de 30 a 60 segundos es habitual) sin desperdiciar ancho de banda.
- Evita saltos innecesarios. Cada bridge, gateway o relé de Node-RED en el camino añade su propio procesamiento y sus colas.
- Haz que los callbacks del suscriptor sean rápidos. Pasa el trabajo pesado a un hilo de trabajo o a una cola para no bloquear el bucle de red.
Solución de problemas de latencia alta o irregular
| Síntoma | Causa probable | Qué revisar |
|---|---|---|
| RTT mínimo alto | Distancia al broker o red de acceso lenta | Ping y traceroute al host del broker |
| Buena mediana, p95 pésimo | Reintentos de Wi-Fi, planificación celular | Prueba en un enlace cableado para comparar |
| La latencia crece con el tiempo | El suscriptor no da abasto, los mensajes se encolan | Duración de los callbacks y recuento de mensajes en vuelo |
| Huecos periódicos de varios segundos | Código bloqueante en el bucle del cliente | sleep largos o E/S síncrona en los callbacks |
| Picos solo al reconectar | Handshake TLS más reanudación de sesión | Estabilidad de la conexión y keep-alive |
| Limitación en el servidor público | Límite de 10 msg/s por cliente | Reduce la tasa de publicación o usa un broker privado |
De la medición a producción
El servidor público es compartido, así que toma sus cifras como indicativas de tu ruta de red y no como un benchmark. Para el tráfico de producción, un broker MQTT gestionado como ConnectMQ te da un endpoint privado y autenticado en el 8883 y el 8084 sin los límites de tasa del público; los detalles de los planes están en la guía de brokers MQTT en la nube. Para repetir la prueba contra tu propio broker de ConnectMQ, añade client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD"), establece el client ID en YOUR_CLIENT_ID y ejecuta con --tls. Para un contexto más amplio sobre el protocolo, consulta el tutorial de MQTT y cómo funciona MQTT.
Empieza con ConnectMQ
Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y apunta el script de latencia hacia él con dos líneas. El cliente integrado en el navegador y el explorador de topics en vivo de ConnectMQ te ayudan a ver exactamente cuándo llega cada mensaje.
Preguntas frecuentes
¿Qué es una buena latencia MQTT?
Depende de la distancia y de la red de acceso. Compara tu RTT mínimo con un ping de red normal al host del broker; si MQTT añade solo un poco por encima, el broker no es tu cuello de botella.
¿QoS 1 hace que MQTT sea más lento que QoS 0?
En un enlace sano, la latencia de entrega es similar porque el broker reenvía antes de que se complete el PUBACK. QoS 1 añade sobrecarga sobre todo cuando se pierden paquetes y hay que reenviarlos.
¿TLS aumenta la latencia MQTT?
TLS añade tiempo de handshake al conectar, pero en una conexión de larga duración la sobrecarga por mensaje es pequeña. Mantener las conexiones abiertas es la mejor forma de minimizar su impacto.
¿Por qué medir la ida y vuelta en lugar de la latencia en un sentido?
La ida y vuelta usa un solo reloj, así que evita los errores de los relojes no sincronizados entre las máquinas del publicador y del suscriptor.



