emqtt-bench (el binario se llama emqtt_bench) es una herramienta open source de benchmarking MQTT de EMQ, escrita en Erlang, capaz de abrir decenas de miles de conexiones y generar carga de publicación y suscripción desde una sola máquina. Tiene tres comandos — conn, sub y pub — y una larga lista de flags. Esta guía explica cada comando, los flags que realmente importan, el uso de TLS y WebSocket, la medición de latencia y cómo leer lo que imprime.
Para el método general — qué medir y cómo planificar un benchmark — consulta pruebas de carga de un broker MQTT. Esta página es la referencia de la herramienta en sí.
Instalar o ejecutar con Docker
La forma más rápida es la imagen de Docker, cuyo entrypoint es emqtt_bench:
docker run --rm -it emqx/emqtt-bench pub --helpPara ejecuciones largas o muy grandes, una instalación nativa evita la sobrecarga de red de Docker. Hay paquetes precompilados en la página de releases del proyecto en GitHub. Compilar desde el código fuente requiere un Erlang/OTP reciente y libatomic:
git clone https://github.com/emqx/emqtt-bench.git
cd emqtt-bench
make # BUILD_WITHOUT_QUIC=1 make si falla la compilación de QUIC
./emqtt_bench conn --helpAntes de cualquier benchmark, comprueba a mano host, puerto y credenciales con un único cliente en el cliente MQTT online. Si falla una conexión, dos mil fallarán más rápido. Los ejemplos usan broker.connectmq.com, un endpoint compartido adecuado para comprobaciones rápidas; ejecuta los benchmarks reales contra tu propia instancia privada de broker cambiando -h y las credenciales.
Opciones comunes a todos los comandos
| Flag | Significado | Por defecto |
|---|---|---|
-h, --host | Host del broker; una lista separada por comas reparte los clientes entre varios hosts | localhost |
-p, --port | Puerto del broker | 1883 |
-V, --version | Versión del protocolo MQTT: 3 (3.1), 4 (3.1.1) o 5 | 5 |
-c, --count | Número de clientes que se inician | 200 |
-i, --interval | Milisegundos entre conexiones nuevas | 10 |
-R, --connrate | Conexiones por segundo; 0 vuelve a usar -i | 0 |
-u, -P | Usuario y contraseña | ninguno |
-k, --keepalive | Keep alive en segundos | 300 |
-C, --clean | Clean session / clean start | true |
-x, --session-expiry | Session expiry interval de MQTT 5 en segundos | 0 |
--prefix | Prefijo del client ID (si se define, no se añade sufijo aleatorio) | <host>_bench_<cmd>_<random>_<n> |
--shortids | Client IDs cortos: el número de secuencia, o prefijo + número | false |
-n, --startnumber | Primer número de secuencia; evita choques de IDs entre varias instancias | 0 |
--ifaddr | Dirección(es) IP locales a usar, separadas por comas | la del sistema |
--reconnect | Intentos de reconexión tras una desconexión | 0 (desactivado) |
--num-retry-connect | Reintentos de la conexión inicial | 0 |
-l, --lowmem | Modo de poca memoria: menos RAM por cliente, más CPU | desactivado |
Hay dos detalles que suelen confundir. Primero, la versión de protocolo por defecto es MQTT 5; pasa -V 4 para medir un broker o una configuración que solo habla 3.1.1. Segundo, el keep alive por defecto de 300 s es mucho mayor que el de la mayoría de dispositivos; ajusta -k al valor de tu flota si pruebas el comportamiento de conexiones inactivas.
Los client IDs deben ser únicos por broker. Si ejecutas varios procesos de emqtt-bench contra el mismo broker, da a cada uno su propio --prefix o rango de -n: los IDs duplicados provocan session takeovers que parecen desconexiones aleatorias. Puedes previsualizar formatos de ID con el generador de client IDs.
conn: capacidad de conexiones
conn solo conecta y mantiene las conexiones. Responde a "¿cuántos clientes concurrentes aguanta el broker y a qué velocidad los acepta?"
docker run --rm -it emqx/emqtt-bench conn \
-h broker.connectmq.com -p 1883 \
-c 5000 -R 500 -k 60 \
-u "$MQTT_USER" -P "$MQTT_PASS"Observa cómo connect_succ sube hasta el objetivo y connect_fail se queda en cero. Una tormenta de reconexiones — todos los dispositivos volviendo tras una caída — se simula subiendo -R. Tienes más escenarios en la guía de pruebas de estrés de conexiones.
sub: subscribers
sub conecta clientes, suscribe cada uno a un topic y cuenta lo que recibe.
| Flag | Significado | Por defecto |
|---|---|---|
-t, --topic | Filtro de topic, con placeholders (ver más abajo) | obligatorio |
-q, --qos | QoS de la suscripción | 0 |
--payload-hdrs | Interpreta las cabeceras añadidas por el publisher (ts, cnt64) | ninguno |
docker run --rm -it emqx/emqtt-bench sub \
-h broker.connectmq.com -p 1883 \
-c 100 -i 5 -q 1 \
-t 'testmqtt/bench/%i/data' \
-u "$MQTT_USER" -P "$MQTT_PASS"pub: publishers
pub conecta clientes y publica a un intervalo fijo por cliente.
| Flag | Significado | Por defecto |
|---|---|---|
-t, --topic | Topic en el que publicar, con placeholders | obligatorio |
-I, --interval_of_msg | Milisegundos entre mensajes, por cliente | 1000 |
-s, --size | Tamaño del payload en bytes | 256 |
-m, --message | Payload literal, o template://<file> | bytes aleatorios |
-q, --qos | QoS de publicación | 0 |
-r, --retain | Activa el flag retain | false |
-L, --limit | Se detiene tras este número de mensajes; 0 = ilimitado | 0 |
-F, --inflight | Máximo de mensajes QoS 1/2 sin confirmar por cliente; 0 = ilimitado | 1 |
-w, --wait-before-publishing | Empieza a publicar solo cuando todos los publishers han intentado conectar | false |
--payload-hdrs | Antepone al payload ts (timestamp) y/o cnt64 (contador) | ninguno |
docker run --rm -it emqx/emqtt-bench pub \
-h broker.connectmq.com -p 1883 \
-c 100 -I 100 -s 512 -q 1 -F 10 -w \
-t 'testmqtt/bench/%i/data' \
-u "$MQTT_USER" -P "$MQTT_PASS"La tasa objetivo es clientes × (1000 / -I). Fíjate en -F: con la ventana inflight por defecto de 1, cada cliente QoS 1 espera cada PUBACK antes de enviar el siguiente mensaje, así que con latencia alta un cliente nunca alcanza su intervalo. Si ves crecer pub_overrun, el cliente no pudo mantener su ritmo; sube -F o baja la tasa. Las estrategias de medición de throughput están en la guía de pruebas de throughput MQTT.
Placeholders de topic
Los placeholders en -t permiten que un solo comando cree muchos topics distintos:
| Placeholder | Se sustituye por |
|---|---|
%i | Número de secuencia del cliente (incluye el desplazamiento de -n) |
%c | Client ID |
%u | El usuario indicado con -u |
%s | Número de secuencia del mensaje (solo pub) |
%rand_N | Entero aleatorio entre 1 y N |
Usar %i en ambos lados — pub -t 'testmqtt/bench/%i/data' y sub -t 'testmqtt/bench/%i/data' con el mismo -c — empareja el publisher N con el subscriber N: enrutamiento uno a uno. Un único subscriber en testmqtt/bench/+/data mide en cambio el fan-in, y muchos subscribers en un topic miden el fan-out. Comprueba tus filtros con el topic matcher.
TLS, WebSockets y QUIC
| Flag | Significado |
|---|---|
-S, --ssl | Activa TLS |
--ssl-version | Fuerza una versión de TLS (implica --ssl) |
--cacertfile | Bundle de CA para verificar el broker |
--certfile, --keyfile | Certificado y clave de cliente (TLS mutuo) |
--ciphers | Restringe las cipher suites |
--ws | Usa el transporte WebSocket |
--quic | Usa MQTT sobre QUIC (en brokers que lo soportan) |
# MQTT sobre TLS
emqtt_bench conn -h broker.connectmq.com -p 8883 --ssl \
--cacertfile /etc/ssl/certs/ca-certificates.crt \
-c 1000 -R 100 -u "$MQTT_USER" -P "$MQTT_PASS"
# MQTT sobre WebSocket seguro
emqtt_bench pub -h broker.connectmq.com -p 8084 --ws --ssl \
-c 50 -I 1000 -t 'testmqtt/bench/%i/ws' -u "$MQTT_USER" -P "$MQTT_PASS"emqtt-bench no tiene un flag aparte para la ruta WebSocket; el cliente emqtt subyacente usa /mqtt, que coincide con el endpoint usado en estos ejemplos. Comparar la misma ejecución de conn en 1883 y 8883 muestra directamente el coste del handshake; consulta pruebas de rendimiento TLS en MQTT.
Medir la latencia
Arranca el subscriber y el publisher con el mismo --payload-hdrs ts. El publisher escribe un timestamp en milisegundos al principio de cada payload; el subscriber lo resta de su propio reloj al recibirlo:
# terminal 1
emqtt_bench sub -h broker.connectmq.com -c 50 -q 1 \
-t 'testmqtt/bench/%i/lat' --payload-hdrs ts -u "$MQTT_USER" -P "$MQTT_PASS"
# terminal 2
emqtt_bench pub -h broker.connectmq.com -c 50 -I 200 -q 1 \
-t 'testmqtt/bench/%i/lat' --payload-hdrs ts -u "$MQTT_USER" -P "$MQTT_PASS"El subscriber imprime entonces una línea publish_latency avg=…ms. Es una media; para percentiles activa --prometheus (expone un histograma e2e_latency) o usa el script de la guía de pruebas de carga. Como el timestamp procede del reloj del publisher, ejecuta ambos en la misma máquina o mantén sincronizados los relojes de las máquinas.
Leer la salida
Cada segundo, emqtt-bench imprime una línea por cada contador que ha cambiado, con el formato <tiempo> <contador> total=<n> rate=<n>/sec:
10s connect_succ total=100 rate=0.00/sec
10s pub total=9871 rate=1000.12/sec
10s pub_succ total=9862 rate=999.40/sec| Contador | Significado |
|---|---|
connect_succ / connect_fail | Conexiones aceptadas / fallidas |
connect_retried, reconnect_succ | Reintentos y reconexiones correctas |
connection_refused, connection_timeout, unreachable | Por qué fallaron las conexiones |
connection_idle | Conexiones que quedaron inactivas |
sub / sub_fail | Suscripciones aceptadas / rechazadas |
pub / pub_succ / pub_fail | Mensajes enviados / confirmados / fallidos |
pub_overrun | Un cliente no pudo mantener su intervalo de publicación |
pub_nosub | El broker indicó que no hay subscribers coincidentes (MQTT 5) |
recv | Mensajes recibidos por los subscribers |
publish_latency | Latencia media extremo a extremo (con --payload-hdrs ts) |
El patrón sano: las tasas de pub y recv coinciden con tu objetivo y entre sí, pub_overrun se queda en cero y no aparecen contadores de fallo. Un connection_timeout o connect_fail que crece cerca de un número redondo (1.024, 65.535) suele indicar límites en el generador de carga. Consulta cualquier reason code del broker con la herramienta de reason codes.
Escalar el generador de carga
- Descriptores de fichero. Sube
ulimit -nmuy por encima de-cantes de ejecuciones grandes deconn. - Puertos. Una IP de origen solo puede abrir unas 64K conexiones hacia una dirección y puerto del broker. Usa
--ifaddr 10.0.0.11,10.0.0.12o varias máquinas, y amplía el rango de puertos locales consysctl. - Memoria. Añade
-lpara números de clientes muy grandes, a costa de más CPU. - CPU. Si el generador supera aproximadamente el 80 % de CPU, tus números describen al generador, no al broker.
Observa el tráfico del benchmark en el cliente online
emqtt-bench es la forma más rápida de poner una carga realista de conexiones y mensajes sobre un broker MQTT desde la línea de comandos. Aprende el puñado de flags que dan forma a la carga — -c, -R, -I, -q, -F y los placeholders de topic —, confirma que TLS y versión de protocolo coinciden con producción, y lee los contadores como tasas comparables entre ejecuciones.
Preguntas frecuentes
¿Qué diferencia hay entre -i y -R en emqtt-bench?
-i (--interval) es la pausa en milisegundos entre conexiones nuevas, 10 por defecto. -R (--connrate) fija en su lugar una tasa de conexiones por segundo; cuando vale 0 (por defecto), emqtt-bench vuelve a usar el intervalo.
¿Cómo mido la latencia con emqtt-bench?
Arranca el subscriber y el publisher con la misma opción --payload-hdrs ts. El publisher antepone a cada payload un timestamp en milisegundos y el subscriber imprime una línea publish_latency con la latencia media extremo a extremo. Ejecuta ambos en una máquina o mantén los relojes sincronizados.
¿Por qué emqtt-bench se detiene en torno a 64.000 conexiones?
Una IP de origen solo puede abrir unas 64K conexiones TCP hacia la misma dirección y puerto del broker. Reparte los clientes entre varias direcciones locales con --ifaddr o ejecuta emqtt-bench en varias máquinas, y sube ulimit -n.



