Enviaste un endpoint. Funciona en tu navegador. Pero no tienes idea de qué sucede cuando 400 personas lo acceden a la vez. ¿La latencia se mantiene constante o el percentil noventa y nueve se dispara? ¿Puede el servidor manejar 1,000 solicitudes por segundo, o colapsa a 300?
wrk responde eso. Es una pequeña herramienta de línea de comandos que envía mucho tráfico HTTP a una URL e informa qué tan rápido respondió el servidor bajo esa carga.
Qué es wrk y cuándo usarlo
wrk es una herramienta moderna de benchmarking HTTP. Genera carga desde una única máquina de múltiples núcleos y mide la latencia y la tasa de solicitudes que devuelve el servidor. Utiliza multithreading más un bucle de eventos escalable (epoll en Linux, kqueue en macOS), por lo que una instancia puede generar mucho tráfico sin necesidad de una flota de máquinas de carga.
Usa wrk cuando quieras números de rendimiento puros:
- ¿Cuántas solicitudes por segundo puede soportar este endpoint?
- ¿Cómo es la latencia en la mediana versus la cola?
- ¿El servidor aguanta durante un período sostenido, o se degrada después de un minuto?
wrk es una herramienta de benchmarking, no una suite de pruebas. Mide la velocidad. No verifica que el cuerpo JSON sea correcto, que el código de estado sea 200, o que el contrato de la API se haya mantenido. Ten en cuenta esa distinción. Volveremos a ella casi al final, porque cambia cómo encajas wrk en un flujo de trabajo de prueba real. Si quieres una imagen más amplia primero, esta guía sobre pruebas de carga de API cubre los conceptos que wrk pone en práctica.
Instalación de wrk
macOS
Homebrew tiene un binario precompilado, que es el camino de menor resistencia:
brew install wrk
En Apple Silicon esto es importante. Compilar desde el código fuente puede causar problemas con LuaJIT ARM64, por lo que el binario de Homebrew te ahorra el dolor de cabeza.
Linux (compilar desde el código fuente)
No hay un paquete apt oficial, así que lo compilas. Instala primero la cadena de herramientas y los encabezados de OpenSSL:
sudo apt-get install build-essential libssl-dev git -y
Luego clona y compila:
git clone https://github.com/wg/wrk.git
cd wrk
make
Esto produce un binario wrk en el directorio actual. Muévelo a tu PATH para que puedas llamarlo desde cualquier lugar:
sudo cp wrk /usr/local/bin
Confirma que se ejecuta:
wrk --version
El comando básico
Así es la forma de cada ejecución de wrk:
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Están sucediendo cuatro cosas. Analicemos las banderas.
-t, --threadsestablece el número de hilos del sistema operativo que wrk inicia. Un buen punto de partida es un hilo por núcleo de CPU. Aquí son 12.-c, --connectionsestablece el total de conexiones HTTP abiertas en todos los hilos. Aquí 400 conexiones se distribuyen entre los 12 hilos. Las conexiones son la forma de simular clientes concurrentes.-d, --durationestablece el tiempo que se ejecuta la prueba. Acepta valores como30s,2mo2h. Aquí son 30 segundos.
Dos banderas más que usarás constantemente:
--latencyimprime un desglose detallado de los percentiles de latencia. Actívala casi siempre. Los promedios ocultan la latencia de cola, y la cola suele ser lo que perjudica a los usuarios.--timeoutregistra una solicitud como agotada si no llega ninguna respuesta dentro de la ventana que establezcas, por ejemplo--timeout 2s. Sin ella, las respuestas lentas pueden distorsionar tus números de latencia.
Una ejecución que usarás a menudo se ve así:
wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Ocho hilos, 200 conexiones, 30 segundos, con la distribución completa de latencia impresa al final.
Lectura de la salida
wrk imprime un informe compacto. Aquí hay una ejecución real contra un pequeño servicio:
Running 5s test @ http://10.135.232.163:3000
2 threads and 5 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.82ms 2.64ms 26.68ms 85.81%
Req/Sec 550.90 202.40 0.98k 68.00%
5494 requests in 5.01s, 1.05MB read
Requests/sec: 1096.54
Transfer/sec: 215.24KB
Léelo de abajo hacia arriba, porque las últimas dos líneas son el titular.
Requests/sec es el rendimiento: cuántas solicitudes completó el servidor por segundo en promedio. Aquí es 1,096. Este es el número que comparas entre ejecuciones y entre cambios de código.
Transfer/sec es el ancho de banda: cuántos datos se movieron por segundo. Útil cuando las cargas útiles son grandes o sospechas que estás limitado por el ancho de banda en lugar de por la CPU.
Ahora la tabla Thread Stats, que describe la distribución, no solo el promedio:
- Fila Latency: la latencia promedio fue de 3.82ms, pero la desviación estándar fue de 2.64ms y el máximo se disparó a 26.68ms. Un máximo alto en relación con el promedio es una señal. Algunas solicitudes son lentas incluso si la mayoría son rápidas.
- Fila Req/Sec: la tasa de solicitudes por hilo, nuevamente con su dispersión.
La columna +/- Stdev te indica qué porcentaje de muestras cayeron dentro de una desviación estándar. Porcentajes más bajos significan una dispersión más amplia y menos predecible.
La línea 5494 requests in 5.01s confirma el volumen total que realmente se generó en la ejecución.
Cuando añades --latency, wrk imprime un bloque de percentiles para que puedas ver la cola directamente:
Latency Distribution
50% 3.21ms
75% 4.86ms
90% 7.09ms
99% 14.13ms
El percentil 99 es el número a observar. Si el 99% de las solicitudes finalizan en 14ms pero tu promedio es de 3.82ms, uno de cada cien usuarios está esperando mucho más de lo que sugiere el promedio. Los promedios mienten sobre las colas. Los percentiles no.
Envío de solicitudes POST y cabeceras personalizadas con un script Lua
Por defecto, wrk envía solicitudes GET. Para enviar un POST, añadir un cuerpo o establecer cabeceras personalizadas, pasas un script Lua con -s.
Crea un archivo llamado post.lua:
wrk.method = "POST"
wrk.body = '{"name": "Ada", "role": "engineer"}'
wrk.headers["Content-Type"] = "application/json"
Tres campos hacen el trabajo. wrk.method establece el verbo HTTP. wrk.body establece el cuerpo de la solicitud. wrk.headers es una tabla donde cada clave es un nombre de cabecera.
Ejecútalo apuntando -s al script:
wrk -t4 -c100 -d30s -s post.lua --latency http://localhost:3000/api/users
Para un POST codificado en formulario en lugar de JSON, el repositorio de wrk incluye este ejemplo exacto:
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
También puedes establecer encabezados con la bandera -H para casos más simples, sin un script:
wrk -t4 -c100 -d30s -H "Authorization: Bearer TOKEN123" --latency http://localhost:3000/api/protected
Usa -H para una o dos cabeceras. Usa un script Lua cuando necesites un cuerpo, un método que no sea GET o lógica por solicitud.
Los límites: wrk no comprueba la corrección
Aquí está la parte que la gente omite. wrk te dice qué tan rápido respondió el servidor. No te dice si la respuesta fue correcta.
Apuntale wrk a un endpoint que devuelve HTTP 500 en cada solicitud, y obtendrás un informe de aspecto limpio con un alto número de solicitudes por segundo. wrk cuenta un intercambio HTTP completado. No afirma el código de estado, no valida el cuerpo de la respuesta contra un esquema, ni confirma que la API hizo lo que se suponía que debía hacer. Los errores pueden incluso parecer rápidos, porque un servidor que rechaza solicitudes temprano hace menos trabajo por solicitud.
Así que wrk responde "¿es lo suficientemente rápido bajo carga?". No puede responder "¿es correcto?". Ambas preguntas importan, y necesitan herramientas diferentes. Un número de carga en un endpoint roto es un número en el que no debes confiar. Esta es exactamente la razón por la que los equipos combinan una herramienta de benchmarking con una suite de pruebas funcionales. Una prueba la velocidad. La otra prueba el comportamiento.
Dónde encajan Apidog y las pruebas funcionales
El flujo de trabajo limpio tiene dos capas, ejecutadas en orden.
Primero, valida el comportamiento. Antes de preocuparte por la velocidad de un endpoint, confirma que es correcto. En Apidog creas escenarios de prueba que envían solicitudes reales y afirman lo que se recibe: códigos de estado, campos JSON, esquema de respuesta y lógica de negocio. Puedes encadenar solicitudes, pasar datos entre pasos y ejecutar el mismo escenario en diferentes entornos. Esta es la capa que detecta el error 500 que wrk felizmente mide.
Luego, evalúa el rendimiento. Una vez que el comportamiento ha sido verificado, ejecuta wrk contra los mismos endpoints para ver cómo se comportan bajo concurrencia y carga sostenida. Apidog también tiene pruebas de rendimiento integradas si prefieres mantener el trabajo funcional y de carga en un solo lugar, pero wrk es una excelente herramienta dedicada para el benchmarking puro en línea de comandos.
La capa funcional se ejecuta en CI, no solo en tu laptop. La CLI de Apidog es sin interfaz gráfica (headless), por lo que se integra en cualquier paso de la pipeline que pueda ejecutar Node. Instálala:
npm install -g apidog-cli
Luego ejecuta un escenario de prueba o suite guardada por ID:
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
-t es el ID del escenario, carpeta o suite a ejecutar. -e es el ID del entorno. -r selecciona los formatos de informe, uno o más de cli, html, json y junit. La salida JUnit se conecta directamente a la mayoría de los sistemas CI para la verificación de aprobación/fallo. Para ejecuciones basadas en datos, añade -d (o --iteration-data) con una ruta de archivo o ID de datos de prueba para iterar el mismo escenario sobre muchas filas de entrada.
La CLI ejecuta escenarios y suites de Apidog guardados. Es headless, no un remitente de solicitudes interactivo, y no es un generador de carga. Es la puerta de verificación de la corrección. wrk es el medidor de velocidad. Ejecuta la puerta de verificación de la corrección en tu pipeline (consulta este tutorial de CI/CD con la CLI de Apidog o la guía de GitHub Actions para una configuración de copiar y pegar), luego realiza el benchmarking con wrk cuando necesites los números de rendimiento. La referencia completa de la CLI cubre el resto de las banderas.
Preguntas frecuentes
¿Cuál es la diferencia entre wrk y ab (ApacheBench)? Ambos envían carga HTTP e informan solicitudes por segundo. wrk es multihilo y usa un bucle de eventos, por lo que genera más carga desde una sola máquina y maneja mejor la alta concurrencia. ab es de un solo hilo. Para cargas pesadas desde una sola máquina, wrk suele escalar más. Ninguno de los dos verifica la corrección de la respuesta.
¿Cuántos hilos y conexiones debo usar? Empieza con un hilo por núcleo de CPU y establece las conexiones al nivel de concurrencia que quieras simular. Si tienes 8 núcleos y quieres modelar 200 clientes concurrentes, prueba -t8 -c200. Observa la máquina cliente. Si wrk mismo está limitado por la CPU, tus números reflejan el límite del generador de carga, no el del servidor. Aumenta las conexiones hasta que el rendimiento deje de subir.
¿Puede wrk probar endpoints HTTPS? Sí. Apúntale a una URL https:// y wrk maneja TLS. Por eso la compilación de Linux necesita libssl-dev. Los handshakes TLS añaden un costo de CPU en ambos extremos, así que espera un rendimiento bruto menor en HTTPS que en HTTP plano.
¿wrk valida el cuerpo de la respuesta o el código de estado? No. wrk cuenta los intercambios HTTP completados y mide el tiempo. No afirma los códigos de estado ni los cuerpos, por lo que un endpoint que devuelve errores aún puede registrar un alto número de solicitudes por segundo. Utiliza una suite de pruebas funcionales, como una ejecutada a través de la CLI de Apidog, para verificar la corrección, luego usa wrk para el rendimiento.
¿Cuánto tiempo debe durar una prueba de carga? Lo suficiente para superar los efectos de calentamiento, como cachés frías y compilación JIT. Unos pocos segundos están bien para una verificación rápida, pero de 30 segundos a unos minutos proporciona números más estables y revela la degradación que solo aparece bajo carga sostenida. Usa -d30s como un valor predeterminado sensato y alárgalo cuando estés buscando fugas lentas.
