Pruebas de brokers

Guía de emqtt-bench: todos los comandos y flags explicados

Guía completa de emqtt-bench: modos conn, pub y sub, los flags importantes, opciones TLS y WebSocket, cabeceras de latencia y cómo leer los contadores.

Actualizado · 10 min de lectura

Guía de emqtt-bench: todos los comandos y flags explicados

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:

Ejecutar con Docker
docker run --rm -it emqx/emqtt-bench pub --help

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

Compilar desde el código fuente
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 --help

Antes 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

FlagSignificadoPor defecto
-h, --hostHost del broker; una lista separada por comas reparte los clientes entre varios hostslocalhost
-p, --portPuerto del broker1883
-V, --versionVersión del protocolo MQTT: 3 (3.1), 4 (3.1.1) o 55
-c, --countNúmero de clientes que se inician200
-i, --intervalMilisegundos entre conexiones nuevas10
-R, --connrateConexiones por segundo; 0 vuelve a usar -i0
-u, -PUsuario y contraseñaninguno
-k, --keepaliveKeep alive en segundos300
-C, --cleanClean session / clean starttrue
-x, --session-expirySession expiry interval de MQTT 5 en segundos0
--prefixPrefijo del client ID (si se define, no se añade sufijo aleatorio)<host>_bench_<cmd>_<random>_<n>
--shortidsClient IDs cortos: el número de secuencia, o prefijo + númerofalse
-n, --startnumberPrimer número de secuencia; evita choques de IDs entre varias instancias0
--ifaddrDirección(es) IP locales a usar, separadas por comasla del sistema
--reconnectIntentos de reconexión tras una desconexión0 (desactivado)
--num-retry-connectReintentos de la conexión inicial0
-l, --lowmemModo de poca memoria: menos RAM por cliente, más CPUdesactivado

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?"

5.000 clientes a 500 conexiones por segundo
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.

FlagSignificadoPor defecto
-t, --topicFiltro de topic, con placeholders (ver más abajo)obligatorio
-q, --qosQoS de la suscripción0
--payload-hdrsInterpreta las cabeceras añadidas por el publisher (ts, cnt64)ninguno
100 subscribers, un topic cada uno
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.

FlagSignificadoPor defecto
-t, --topicTopic en el que publicar, con placeholdersobligatorio
-I, --interval_of_msgMilisegundos entre mensajes, por cliente1000
-s, --sizeTamaño del payload en bytes256
-m, --messagePayload literal, o template://<file>bytes aleatorios
-q, --qosQoS de publicación0
-r, --retainActiva el flag retainfalse
-L, --limitSe detiene tras este número de mensajes; 0 = ilimitado0
-F, --inflightMáximo de mensajes QoS 1/2 sin confirmar por cliente; 0 = ilimitado1
-w, --wait-before-publishingEmpieza a publicar solo cuando todos los publishers han intentado conectarfalse
--payload-hdrsAntepone al payload ts (timestamp) y/o cnt64 (contador)ninguno
100 publishers, 10 msg/s cada uno = 1.000 msg/s
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:

PlaceholderSe sustituye por
%iNúmero de secuencia del cliente (incluye el desplazamiento de -n)
%cClient ID
%uEl usuario indicado con -u
%sNúmero de secuencia del mensaje (solo pub)
%rand_NEntero 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

FlagSignificado
-S, --sslActiva TLS
--ssl-versionFuerza una versión de TLS (implica --ssl)
--cacertfileBundle de CA para verificar el broker
--certfile, --keyfileCertificado y clave de cliente (TLS mutuo)
--ciphersRestringe las cipher suites
--wsUsa el transporte WebSocket
--quicUsa MQTT sobre QUIC (en brokers que lo soportan)
TLS y WebSocket seguro
# 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:

Latencia extremo a extremo
# 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:

Salida típica de pub
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
ContadorSignificado
connect_succ / connect_failConexiones aceptadas / fallidas
connect_retried, reconnect_succReintentos y reconexiones correctas
connection_refused, connection_timeout, unreachablePor qué fallaron las conexiones
connection_idleConexiones que quedaron inactivas
sub / sub_failSuscripciones aceptadas / rechazadas
pub / pub_succ / pub_failMensajes enviados / confirmados / fallidos
pub_overrunUn cliente no pudo mantener su intervalo de publicación
pub_nosubEl broker indicó que no hay subscribers coincidentes (MQTT 5)
recvMensajes recibidos por los subscribers
publish_latencyLatencia 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 -n muy por encima de -c antes de ejecuciones grandes de conn.
  • 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.12 o varias máquinas, y amplía el rango de puertos locales con sysctl.
  • Memoria. Añade -l para 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.