"Mensajes por segundo" es la cifra MQTT más citada y la peor entendida. Un broker puede aceptar 50.000 msg/s de los publishers y aun así entregar solo la mitad, o entregarlos todos con dos segundos de retraso. Una prueba de throughput MQTT útil mide la tasa que el sistema puede sostener —de entrada y de salida— mientras la latencia se mantiene plana y no se pierde nada, con tus propios payloads y tu estructura de topics.
Esta guía define las cifras, muestra cómo encontrar la tasa sostenible paso a paso y explica las señales de backpressure que indican que la has superado. Para la visión general de conexiones y latencia, consulta pruebas de carga de un broker MQTT.
Entrada, salida y fan-out
| Término | Significado | Dónde se mide |
|---|---|---|
| Tasa de entrada (ingress) | Paquetes PUBLISH por segundo que el broker acepta de los publishers | Lado publisher (enviados / confirmados) |
| Tasa de salida (egress) | Paquetes PUBLISH por segundo que el broker entrega a los subscribers | Lado subscriber (recibidos) |
| Factor de fan-out | Número medio de subscribers por mensaje | Salida ÷ entrada |
| Tasa sostenible | Entrada máxima con la que la salida sigue el ritmo, la latencia es estable y los errores son cero | Ambos lados, durante minutos |
El factor de fan-out es lo que la gente olvida. Un publisher a 1.000 msg/s con 20 subscribers en el topic supone 20.000 msg/s de salida. El trabajo del broker se parece más a la salida que a la entrada, así que indica siempre ambas. Para los casos extremos, consulta la prueba de fan-out y la prueba de fan-in.
Throughput pico frente a sostenible
Cualquier sistema puede absorber durante un rato una ráfaga por encima de su capacidad real: los buffers de socket, las colas del cliente y las del broker se llenan y ocultan el problema. Una prueba de cinco segundos mide esos buffers, no el broker. La tasa sostenible solo aparece cuando mantienes cada nivel de carga el tiempo suficiente para que las colas lleguen a un estado estable, normalmente unos minutos.
El método es una rampa escalonada:
- Empieza con una tasa que seguro que el sistema aguanta (por ejemplo, el 20 % del pico esperado).
- Mantenla 2–5 minutos y registra entrada, salida, latencia p99 y errores.
- Aumenta en un paso fijo (por ejemplo +25 %) y vuelve a mantener.
- Para cuando la salida deje de seguir a la entrada, la latencia p99 siga subiendo dentro de un mismo paso o aparezcan errores.
- El último paso limpio es tu tasa sostenible. Planifica producción con un margen cómodo por debajo.
Si dibujas tasa ofrecida frente a latencia, obtienes una línea plana seguida de un "codo" en el que la latencia se curva hacia arriba. Ese codo es la cifra que buscas, no la tasa máxima que podía empujar el generador de carga.
La rampa con emqtt-bench
emqtt-bench genera tasas altas desde una sola máquina. Arranca primero los subscribers y después los publishers a tasas crecientes. El intervalo de publicación -I es por cliente y en milisegundos, así que la tasa total es clientes × 1000 / I:
docker run --rm -it emqx/emqtt-bench sub \
-h broker.connectmq.com -p 1883 \
-c 50 -t 'testmqtt/tput/%i' -q 1 \
-u "$MQTT_USER" -P "$MQTT_PASS"docker run --rm -it emqx/emqtt-bench pub \
-h broker.connectmq.com -p 1883 \
-c 50 -I 50 -t 'testmqtt/tput/%i' -s 256 -q 1 \
-u "$MQTT_USER" -P "$MQTT_PASS"En los pasos siguientes baja -I (50 → 40 → 30 → 20 → 10 ms) o añade clientes. emqtt-bench imprime cada segundo las tasas enviadas y recibidas; cópialas en una hoja de cálculo por paso. Como aquí cada publisher tiene su propio subscriber, el factor de fan-out es 1 y la salida debería igualar a la entrada.
Una sonda en Python para huecos y retraso
Los contadores de tasa ocultan dos cosas: qué mensajes faltaron y cuánto se retrasaron los demás. Ejecuta esta pequeña sonda con paho-mqtt 2.x junto al benchmark. Publica mensajes numerados y con marca de tiempo a una tasa modesta e informa cada segundo de huecos y retraso; que el retraso de la sonda suba mientras corre el bench es tu señal de backpressure:
import json, time, uuid
import paho.mqtt.client as mqtt
HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/tput/probe-{uuid.uuid4().hex[:6]}"
RATE = 100 # mensajes de sonda por segundo
stats = {"n": 0, "last": -1, "gaps": 0, "lag": []}
def on_message(client, userdata, msg):
m = json.loads(msg.payload)
if m["i"] > stats["last"] + 1:
stats["gaps"] += m["i"] - stats["last"] - 1
stats["last"] = max(stats["last"], m["i"])
stats["n"] += 1
stats["lag"].append((time.perf_counter() - m["t"]) * 1000)
def client(name):
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{name}-{uuid.uuid4().hex[:6]}")
c.connect(HOST, PORT)
c.loop_start()
return c
sub = client("probe-sub")
sub.on_message = on_message
sub.subscribe(TOPIC, qos=1)
pub = client("probe-pub")
time.sleep(1)
i, next_report = 0, time.time() + 1
while True:
pub.publish(TOPIC, json.dumps({"i": i, "t": time.perf_counter()}), qos=1)
i += 1
time.sleep(1 / RATE)
if time.time() >= next_report:
lag = sorted(stats["lag"]) or [0]
print(f"rx {stats['n']:>5}/s gaps {stats['gaps']} "
f"lag p50 {lag[len(lag)//2]:.1f} ms p99 {lag[int(len(lag)*0.99)-1]:.1f} ms")
stats.update(n=0, lag=[])
next_report += 1Un solo proceso Python no va a generar decenas de miles de mensajes por segundo; ese es el trabajo de la herramienta de benchmark. La sonda es un termómetro, no una estufa.
Reconocer el backpressure
MQTT no tiene una señal global de control de flujo; el backpressure aparece como colas que crecen en algún sitio. Aprende dónde mirar:
| Señal | Dónde vive | Qué significa |
|---|---|---|
| La tasa enviada del publisher cae por debajo del objetivo | Ventana inflight del cliente llena (QoS 1/2) | Los PUBACK vuelven más despacio de lo que publicas: el broker o el camino están saturados |
| El retraso de la sonda sube sin parar durante un paso | Cola del broker o del subscriber | La salida no da abasto; estás por encima de la tasa sostenible |
| Huecos en la sonda con QoS 0 | El broker descarta mensajes para clientes lentos | Límite de cola alcanzado; el broker se quita carga |
| Subscribers desconectados con errores de cola o cuota | Límites por cliente del broker | Consulta cola de mensajes llena y consumidores lentos |
| CPU del generador de carga al 100 % | Tu máquina de prueba | El resultado mide el cliente, no el broker |
Con QoS 1 y 2 la ventana inflight es el limitador clave: un cliente solo puede tener cierto número de mensajes sin confirmar (max_inflight_messages_set() en paho, Receive Maximum en MQTT 5). Con 20 mensajes en vuelo y un round trip de 50 ms, una conexión se queda en torno a 400 msg/s por rápido que sea el broker. Por eso las pruebas sobre enlaces lejanos salen peor que las de la misma región, y por eso lo que sube el techo son más conexiones, no un bucle más rápido. Receive Maximum y control de flujo explica el mecanismo.
De msg/s a ancho de banda
El throughput también es un presupuesto de red. Un PUBLISH de MQTT cuesta aproximadamente: cabecera fija (2–5 bytes) + longitud del topic + 2 bytes + identificador de paquete (2 bytes con QoS > 0) + propiedades MQTT 5 + payload. Luego suma las cabeceras TCP/IP y, si se usa, la sobrecarga de los registros TLS. QoS 1 añade un PUBACK de 4 bytes en sentido contrario.
Ejemplo: payload de 256 bytes, topic de 20 bytes, QoS 1 → unos 285 bytes por PUBLISH antes de TCP/IP. A 5.000 msg/s de salida son unos 1,4 MB/s, es decir, unos 11–12 Mbit/s de tráfico MQTT antes de la sobrecarga de IP y TLS. En mensajes pequeños domina la sobrecarga, así que agrupar lecturas en un solo mensaje suele subir el throughput efectivo más que cualquier ajuste del broker. La calculadora de tamaño de paquete hace las cuentas para tu topic y payload exactos.
Documentar el resultado
Un resultado de throughput solo es reutilizable si indica sus condiciones. Registra como mínimo: versión y configuración del broker, QoS, tamaño de payload, número de topics, factor de fan-out, número de conexiones publisher y subscriber, TLS activado o no, duración de cada paso y región del generador de carga. Después informa de las tasas sostenibles de entrada y salida con su latencia p99, no del pico. Cuando compares ejecuciones más adelante, interpretar resultados de benchmark cubre las trampas habituales.
Antes de la rampa, haz un smoke test de tus topics con el cliente MQTT online para confirmar credenciales y ACLs: un error de permisos bajo carga se parece exactamente a un problema de throughput.
Una prueba de throughput es la búsqueda del codo, no una carrera por la cifra más grande. Sube en pasos, mantén cada paso lo suficiente, vigila retraso y huecos en lugar de solo contadores, e informa de la salida junto a la entrada: esa es la cifra con la que puedes planificar capacidad.
Preguntas frecuentes
¿Qué cifra de throughput MQTT es buena?
No hay una cifra universal: depende del tamaño del payload, el QoS, el fan-out, TLS, la persistencia y el hardware. La cifra útil es la tasa más alta que tu broker sostiene con tu propio patrón de tráfico mientras la latencia se mantiene plana y no se pierde nada.
¿Por qué mi tasa recibida es menor que la enviada?
O bien los mensajes se están encolando en algún sitio (ventanas inflight, colas del broker, buffers del cliente) o se están descartando. Si la diferencia crece con el tiempo, estás por encima de la tasa sostenible. Con QoS 0, una diferencia creciente suele significar pérdidas.
¿El QoS afecta al throughput MQTT?
Sí. QoS 1 añade un PUBACK por mensaje y por salto, y QoS 2 añade tres paquetes extra; ambos están limitados por la ventana inflight. Espera tasas pico menores con QoS más alto, sobre todo en enlaces con mucha latencia.



