Cómo medir el rendimiento y el límite de concurrencia a través de un proxy con k6 y JMeter
Contenido del artículo
- Introducción: por qué conocer el límite con antelación y no durante una descarga masiva
- Preparación previa: herramientas, requisitos y accesos
- Conceptos básicos en palabras sencillas
- Paso 1: prepara el objetivo de prueba y los datos del proxy
- Paso 2: entiende la metodología de carga correcta
- Paso 3: k6: script listo con proxy y escalones de carga
- Paso 4: jmeter: el mismo escenario configurando el proxy en el plan de prueba
- Paso 5: distinguir el límite del proxy del límite del cliente y del límite del servidor: tres verificaciones
- Paso 6: qué hacer con el resultado: hilos, pool y tiempo de descarga
- Paso 7: ética de la carga y modos suaves
- Verificación del resultado: lista de verificación de preparación
- Errores típicos y soluciones
- Opciones adicionales y optimización
- Faq: preguntas frecuentes sobre la medición del rendimiento a través de un proxy
- Conclusión
Trabajar a través de un proxy casi siempre lleva a una pregunta: cuántas solicitudes paralelas puede soportar realmente la combinación "tu cliente - proxy - servidor de destino" antes de que las latencias se disparen y los errores empiecen a aparecer. Esta guía te enseñará a medir esto con antelación, y no en plena descarga masiva, cuando el tiempo de inactividad se mide en horas.
Introducción: por qué conocer el límite con antelación y no durante una descarga masiva
Imagina una situación típica. Lanzas una tarea grande de recopilación de datos públicos a través de un pool de proxies. Todo va bien en los primeros miles de solicitudes, pero luego las latencias aumentan, algunas solicitudes empiezan a fallar por tiempo de espera agotado y no entiendes: ¿la culpa es de tu código, del proxy o del servidor de destino? Para ese momento, ya has perdido tiempo y, posiblemente, datos.
Es mucho más sensato realizar una prueba de carga controlada de antemano y encontrar el punto de degradación. Así podrás elegir un número seguro de hilos y planificar correctamente el tiempo de descarga.
Qué obtendrá el lector al final
Después de completar la guía, podrás hacer lo siguiente por tu cuenta:
- Crear un escenario de carga correcto en k6 y ejecutarlo a través de un proxy.
- Repetir el mismo escenario en JMeter configurando el proxy en el plan de prueba.
- Leer el informe: entender el RPS (solicitudes por segundo), las latencias por percentiles y la tasa de errores.
- Distinguir el límite del proxy del límite de tu cliente y del límite del servidor de destino.
- Convertir los resultados en número de hilos, tamaño del pool y tiempo de descarga esperado.
Para quién es esta guía
El material está diseñado para ingenieros, analistas de datos y desarrolladores de nivel intermedio. Si ya has escrito scripts simples en JavaScript o has ejecutado programas desde la línea de comandos, te sentirás cómodo. Para principiantes, explicamos cada término en palabras sencillas.
Qué necesitas saber de antemano
Basta con habilidades básicas: saber abrir una terminal, instalar un programa y editar un archivo de texto. Conocer HTTP a nivel de "qué es una solicitud y una respuesta" será una ventaja, pero no es obligatorio.
Cuánto tiempo necesitarás
Instalar las herramientas tomará unos 30-40 minutos. Ejecutarás tu primera prueba funcional en k6 en una hora. El ciclo completo con JMeter y el análisis tomará de 3 a 4 horas de trabajo tranquilo.
⚠️ Atención: Todos los ejemplos de la guía están destinados a probar tu propio entorno o recursos sobre los que tengas permiso explícito para cargar. Las pruebas de carga contra servicios de terceros sin consentimiento son inaceptables. Hablaremos sobre la ética en detalle al final.
Preparación previa: herramientas, requisitos y accesos
Antes de medir, debes preparar el entorno de trabajo. Aquí enumeraremos todo lo necesario y mostraremos cómo instalar cada componente.
Herramientas y accesos necesarios
- k6: una herramienta ligera de pruebas de carga; los escenarios se escriben en JavaScript.
- Apache JMeter: una herramienta clásica con interfaz gráfica basada en Java.
- Java 17 o superior: necesario para ejecutar JMeter.
- Acceso a un proxy de Proxeon: dirección, puerto, usuario y contraseña. Estos datos se obtienen en el panel de control del servicio.
- Objetivo de prueba: tu propio entorno o un recurso acordado.
Requisitos del sistema
Para un trabajo cómodo, una máquina con 4 núcleos de CPU y 8 GB de RAM es suficiente. Para cargas altas (miles de usuarios virtuales), se recomiendan 8 núcleos y 16 GB. El sistema operativo puede ser Windows, macOS o Linux; todas las herramientas son multiplataforma.
Consejo: Ejecuta el generador de carga en una máquina separada o en un servidor en la nube. Así no confundirás la carga en tu portátil con la carga en el proxy.
Instalación de k6
- Consulta el método de instalación oficial para tu sistema: en macOS, usa el gestor de paquetes Homebrew con el comando brew install k6.
- En Windows, usa el gestor de paquetes Chocolatey: choco install k6.
- En Linux, descarga el paquete desde el repositorio del proyecto e instálalo con el gestor de paquetes del sistema.
- Verifica la instalación con el comando k6 version: deberías ver el número de versión.
✅ Verificación: Si el comando k6 version muestra una línea con la versión y no da el error "comando no encontrado", la herramienta está instalada correctamente.
Instalación de Java y JMeter
- Instala OpenJDK versión 17 o superior y verifica con el comando java -version.
- Descarga el archivo de Apache JMeter desde el sitio oficial del proyecto en la sección de descargas.
- Descomprime el archivo en una carpeta conveniente, por ejemplo, en tu directorio de inicio.
- Navega a la subcarpeta bin dentro del JMeter descomprimido.
- Ejecuta el archivo jmeter (en Linux y macOS) o jmeter.bat (en Windows).
- Espera a que aparezca la ventana gráfica con el árbol del plan de prueba a la izquierda.
✅ Verificación: Si se abre la ventana de JMeter con el elemento "Test Plan" en el árbol de la izquierda, todo está listo.
Copias de seguridad y preparación del entorno
Si estás probando tu propio servicio, haz una instantánea o copia de seguridad de los datos del entorno. La carga no debe dañar la producción. Si es posible, usa una copia del entorno en lugar del servidor en producción.
Conceptos básicos en palabras sencillas
Para leer los informes y no confundirte con los términos, repasemos los conceptos clave.
Rendimiento y RPS
Rendimiento es cuánto trabajo útil realiza el sistema por unidad de tiempo. En el contexto de HTTP, se mide comúnmente en RPS (solicitudes por segundo). Cuanto mayor sea el RPS con latencias aceptables, mejor.
Latencia y percentiles
Latencia es el tiempo desde que se envía una solicitud hasta que se recibe la respuesta. Un solo valor promedio es engañoso porque las solicitudes lentas poco frecuentes se diluyen en él. Por eso se usan percentiles.
El percentil p95 significa que el 95 por ciento de las solicitudes fueron más rápidas que este valor, y el 5 por ciento más lentas. El percentil p99 muestra el comportamiento de las solicitudes más lentas. Precisamente p95 y p99 reflejan mejor la experiencia real bajo carga.
Tasa de errores y punto de degradación
Tasa de errores es el porcentaje de solicitudes que finalizaron sin éxito: tiempos de espera agotados, rechazos de conexión, códigos de respuesta 5xx. Punto de degradación es el punto donde el aumento de carga deja de incrementar el RPS, pero las latencias y los errores aumentan bruscamente. Ese es el límite que buscamos.
Concurrencia y usuarios virtuales
Concurrencia es cuántas solicitudes se ejecutan simultáneamente. En k6 se define mediante VU (usuarios virtuales); en JMeter, mediante el número de hilos en el grupo de hilos (Thread Group). Un VU o hilo envía solicitudes de forma secuencial, y muchos crean una carga paralela.
El proxy en la cadena de carga
El proxy es un nodo intermediario a través del cual pasan tus solicitudes. Tiene su propio límite de conexiones simultáneas y de ancho de banda. Nuestro objetivo es entender en qué nivel de concurrencia este nodo se convierte en un cuello de botella.
Consejo: Ten presente la ley de Little: el número promedio de solicitudes simultáneas es aproximadamente igual al RPS multiplicado por la latencia promedio en segundos. Te ayuda a estimar rápidamente la concurrencia necesaria.
Paso 1: Prepara el objetivo de prueba y los datos del proxy
Objetivo de la etapa: obtener una URL de destino funcional y una cadena de conexión al proxy correctamente formateada.
- Define la URL del objetivo que vas a cargar. Digamos que es tu propio entorno, por ejemplo, una dirección como https://stend.local/api/health.
- Asegúrate de que el objetivo responda de forma rápida y estable a una solicitud individual a través de un navegador normal o la utilidad curl.
- Toma los datos del proxy de Proxeon desde tu panel de control: host, puerto, nombre de usuario y contraseña.
- Construye la cadena de conexión con el formato http://USUARIO:CONTRASEÑA@HOST:PUERTO. Por ejemplo: http://user:pass@proxy.proxeon.net:8000.
- Verifica el proxy con una solicitud individual usando curl, reemplazando el ejemplo con tus datos.
Ejemplo de solicitud de verificación:
curl -x http://user:pass@proxy.proxeon.net:8000 -H "Accept: application/json" https://stend.local/api/health⚠️ Atención: Nunca almacenes el usuario y la contraseña del proxy directamente en el código que vaya a un repositorio. Usa variables de entorno. Lo mostraremos en los siguientes pasos.
Resultado esperado: curl a través del proxy devuelve una respuesta correcta de tu objetivo sin errores de conexión.
Posibles problemas: si curl se queda colgado, verifica el puerto y el firewall. Si aparece un error de autorización 407, revisa el usuario y la contraseña.
✅ Verificación: Ves el cuerpo de la respuesta del objetivo, obtenido a través del proxy. Esto significa que la cadena funciona.
Paso 2: Entiende la metodología de carga correcta
Objetivo de la etapa: entender por qué una ráfaga única es inútil y cómo construir un perfil de carga correcto.
Por qué una ráfaga única engaña
Si lanzas mil solicitudes de una sola vez, obtendrás un número bonito pero sin sentido. Esa ráfaga no muestra el comportamiento sostenido. El sistema puede absorber un pico gracias a los buffers y luego degradarse. O, por el contrario, puede ahogarse al inicio debido a conexiones en frío.
Las tres fases de una prueba correcta
Una prueba de carga correcta consta de tres fases:
- Calentamiento (warm-up). Durante los primeros 30-60 segundos, subimos la carga gradualmente. Se calientan los pools de conexiones, las cachés de DNS y las estructuras internas. Los datos del calentamiento no se incluyen en las estadísticas finales.
- Crecimiento escalonado (ramp-up steps). Aumentamos la concurrencia en escalones: por ejemplo, 10, 25, 50, 100, 200 VU, manteniendo cada escalón durante 1-2 minutos. Así vemos en qué escalón comienza la degradación.
- Meseta estable (steady state). Fijamos la carga en un nivel ligeramente por debajo del límite y la mantenemos durante 5-10 minutos. La meseta muestra si el sistema es estable bajo carga constante, si hay fugas y acumulación de colas.
Consejo: Nunca saques conclusiones de los primeros 30 segundos de la prueba. Deja que el sistema alcance un estado estable; solo entonces mires los números.
Qué registrar en cada escalón
- El RPS alcanzado.
- Las latencias p50, p95, p99.
- La tasa de errores.
- El número de VU o hilos activos.
El punto de degradación se determina así: encuentra el escalón a partir del cual el RPS dejó de crecer y p95 y la tasa de errores comenzaron a aumentar bruscamente. El escalón anterior es tu punto de trabajo seguro.
✅ Verificación: Puedes explicar con tus propias palabras en qué se diferencia el calentamiento de la meseta y por qué es necesario el crecimiento escalonado.
Paso 3: k6: script listo con proxy y escalones de carga
Objetivo de la etapa: crear y ejecutar un script funcional de k6 que pase por calentamiento, escalones y meseta a través de un proxy.
Cómo funciona k6 con un proxy
k6 lee la dirección del proxy de las variables de entorno HTTP_PROXY y HTTPS_PROXY. Esto es conveniente: no necesitas escribir los datos en el script, solo pasarlos al ejecutar.
Script listo load-test.js
Crea un archivo load-test.js y pega el siguiente código. Reemplaza la URL del objetivo por la tuya.
import http from 'k6/http'; import { check, sleep } from 'k6'; import { Trend, Rate, Counter } from 'k6/metrics'; const latency = new Trend('req_latency', true); const errors = new Rate('req_errors'); const ok = new Counter('req_ok'); export const options = { discardResponseBodies: true, thresholds: { req_errors: ['rate<0.02'], http_req_duration: ['p(95)<1500', 'p(99)<3000'], }, scenarios: { ramp: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '1m', target: 10 }, { duration: '2m', target: 25 }, { duration: '2m', target: 50 }, { duration: '2m', target: 100 }, { duration: '2m', target: 200 }, { duration: '5m', target: 200 }, { duration: '1m', target: 0 }, ], gracefulRampDown: '30s', }, }, }; const TARGET = __ENV.TARGET_URL || 'https://stend.local/api/health'; export default function () { const res = http.get(TARGET, { timeout: '10s', tags: { name: 'target' } }); latency.add(res.timings.duration); const good = check(res, { 'status is 200': (r) => r.status === 200 }); errors.add(!good); if (good) ok.add(1); sleep(0.5); }Cómo ejecutar el script a través del proxy
- Abre una terminal en la carpeta del script.
- Define las variables de entorno con la dirección del proxy y el objetivo. En Linux y macOS, usa el comando export.
- Ejecuta k6 con el comando para ejecutar el script.
Ejemplo de ejecución en Linux y macOS:
export HTTP_PROXY=http://user:pass@proxy.proxeon.net:8000 export HTTPS_PROXY=http://user:pass@proxy.proxeon.net:8000 export TARGET_URL=https://stend.local/api/health k6 run load-test.jsEn Windows con PowerShell, las variables se definen con la sintaxis $env:
$env:HTTP_PROXY="http://user:pass@proxy.proxeon.net:8000"; $env:HTTPS_PROXY="http://user:pass@proxy.proxeon.net:8000"; $env:TARGET_URL="https://stend.local/api/health"; k6 run load-test.jsConsejo: El parámetro discardResponseBodies desactiva el almacenamiento de los cuerpos de respuesta en memoria. Esto reduce la carga sobre el propio generador y ayuda a no confundir su sobrecarga con el límite del proxy.
Lectura del informe de k6
Al finalizar la prueba, k6 muestra un resumen. Presta atención a las líneas clave:
- http_reqs: número total de solicitudes y RPS promedio entre paréntesis. Este es tu rendimiento.
- http_req_duration: latencias, donde se muestran avg, min, med, max y los percentiles p(90), p(95).
- req_errors y http_req_failed: tasa de errores.
- vus: número de usuarios virtuales en el momento.
La línea de thresholds mostrará si tus umbrales se cumplieron. Si aparece una cruz junto a la línea, el umbral se violó, lo que significa que en esta configuración el límite ya se alcanzó o superó.
⚠️ Atención: Los valores de target en stages deben ajustarse a tu sistema. El valor 200 VU es solo un ejemplo. Si tu objetivo o el límite de tu plan de proxy es menor, comienza con escalones más modestos, por ejemplo, hasta 50.
Resultado esperado: la prueba finalizó y ves un resumen con RPS, percentiles y tasa de errores.
Posibles problemas: si todas las solicitudes fallan, verifica las variables del proxy. Si el generador consume el 100 por ciento de la CPU, reduce el número de VU o ejecuta la prueba en una máquina más potente.
✅ Verificación: En el resumen de k6, http_reqs es distinto de cero y la tasa de errores está por debajo de tu umbral al menos en los primeros escalones.
Paso 4: JMeter: el mismo escenario configurando el proxy en el plan de prueba
Objetivo de la etapa: crear un plan equivalente en JMeter con proxy, escalones y recopilación de métricas.
Crear un grupo de hilos
- En JMeter abierto, haz clic derecho en el elemento Test Plan.
- Selecciona Add - Threads (Users) - Thread Group.
- En el campo Number of Threads, define el número máximo de hilos, por ejemplo, 200.
- En el campo Ramp-up period, indica el tiempo para alcanzar la carga completa en segundos, por ejemplo, 540, para replicar el crecimiento escalonado de k6.
- En el campo Loop Count, marca la casilla Infinite; la duración la definiremos con un temporizador y el planificador.
Configurar el proxy en HTTP Request Defaults
Para no escribir el proxy en cada solicitud, lo definimos una sola vez.
- Haz clic derecho en Thread Group y selecciona Add - Config Element - HTTP Request Defaults.
- En el elemento abierto, busca la pestaña con la configuración del servidor proxy.
- En el campo Server Name or IP (en el bloque Proxy), ingresa el host del proxy, por ejemplo, proxy.proxeon.net.
- En el campo Port Number (Proxy), ingresa el puerto, por ejemplo, 8000.
- En los campos Username y Password (Proxy), ingresa tu usuario y contraseña.
Añadir la solicitud HTTP
- Haz clic derecho en Thread Group y selecciona Add - Sampler - HTTP Request.
- En el campo Protocol, escribe https.
- En el campo Server Name or IP, escribe el dominio del objetivo, por ejemplo, stend.local.
- En el campo Path, escribe la ruta, por ejemplo, /api/health.
- En el campo Method, deja GET.
Añadir una pausa entre solicitudes
- Haz clic derecho en HTTP Request y selecciona Add - Timer - Constant Timer.
- En el campo Thread Delay, ingresa 500 milisegundos para replicar el sleep de k6.
Configurar la recopilación de métricas
- Haz clic derecho en Thread Group y añade Add - Listener - Summary Report.
- Añade también Add - Listener - Aggregate Report: muestra los percentiles.
- Para pruebas largas, no uses listeners gráficos; consumen memoria. En su lugar, guarda los resultados en un archivo.
Consejo: Para pruebas pesadas, ejecuta JMeter en modo no gráfico desde la terminal. Así el generador dedica recursos a la carga, no a dibujar gráficos.
Ejemplo de ejecución en modo no gráfico:
jmeter -n -t plan.jmx -l results.jtl -e -o reportAquí, -n ejecuta sin interfaz gráfica, -t indica el archivo del plan, -l el archivo con resultados crudos y -e -o crea un informe HTML en la carpeta report.
Lectura del informe de JMeter
Abre index.html de la carpeta report. Indicadores clave:
- Throughput: rendimiento en solicitudes por segundo.
- Response Times Percentiles: gráfico de percentiles de latencia.
- Error percentage: tasa de errores.
- Active Threads Over Time: cómo creció la concurrencia.
⚠️ Atención: En la autenticación del proxy, JMeter a veces requiere configuración adicional de autorización básica. Si ves muchos errores 407, añade el elemento HTTP Authorization Manager con los datos del proxy.
Resultado esperado: el informe HTML de JMeter se abre y muestra throughput, percentiles y tasa de errores.
✅ Verificación: Los valores de throughput y percentiles en JMeter son comparables con los resultados de k6 con los mismos escalones. Las pequeñas diferencias son normales.
Paso 5: Distinguir el límite del proxy del límite del cliente y del límite del servidor: tres verificaciones
Objetivo de la etapa: determinar exactamente cuál de los tres nodos se convirtió en el cuello de botella.
Cuando veas degradación, es importante entender su origen. Realiza tres verificaciones de control.
Verificación 1: ¿Tu cliente está al límite?
- Durante la prueba, abre el monitor del sistema en la máquina generadora.
- Supervisa el uso de CPU y memoria RAM del proceso k6 o JMeter.
- Verifica el límite de descriptores de archivo en Linux con el comando ulimit -n.
Si la CPU del generador está cerca del 100 por ciento o llegaste al límite de descriptores, la culpa es del cliente, no del proxy. Aumenta el límite de descriptores, reduce el número de VU o usa una máquina más potente.
Consejo: Un signo de sobrecarga del cliente es el aumento de latencias junto con el aumento de la carga de CPU del generador con RPS constante. El proxy no tiene nada que ver aquí.
Verificación 2: ¿El servidor de destino está al límite?
- Ejecuta una prueba corta directamente, sin proxy, eliminando las variables HTTP_PROXY y HTTPS_PROXY.
- Compara el RPS y los percentiles con los resultados a través del proxy.
Si tanto directo como a través del proxy el límite de RPS es casi el mismo, el cuello de botella está en el servidor de destino. El proxy solo añade una pequeña latencia fija.
⚠️ Atención: La prueba directa hazla solo contra tu propio entorno. No cargues recursos de terceros sin permiso, ni siquiera con fines de diagnóstico.
Verificación 3: Aislar el propio proxy
- Levanta en tu entorno un stub ligero que responda inmediatamente con 200 OK y cuerpo vacío.
- Ejecuta la prueba a través del proxy contra ese stub.
El stub responde casi instantáneamente, por lo que las latencias y el límite de RPS ahora dependen principalmente del proxy y la red. Si el RPS se topa con un techo precisamente en el stub, has encontrado el límite del proxy.
Resumamos la lógica en una tabla simple de decisiones con palabras:
- CPU del generador alta, RPS no crece: límite del cliente.
- Directo y a través del proxy igual: límite del servidor de destino.
- A través del proxy con un stub rápido el RPS se topó: límite del proxy.
Resultado esperado: puedes nombrar el nodo concreto que limita tu cadena.
✅ Verificación: Realizaste las tres verificaciones y puedes justificar dónde está exactamente el cuello de botella.
Paso 6: Qué hacer con el resultado: hilos, pool y tiempo de descarga
Objetivo de la etapa: convertir los números de la prueba en configuraciones prácticas para tu tarea real.
Elegir un número seguro de hilos
Toma el escalón antes del punto de degradación. Supongamos que con 100 VU obtuviste 180 RPS estables, p95 de unos 900 ms y errores por debajo del 1 por ciento, mientras que con 200 VU el RPS no creció, pero p95 saltó a 4 segundos. Entonces, el punto de trabajo es alrededor de 100 VU, y para tener margen, toma 80-90.
Calcular el tamaño del pool de proxies
Si un nodo proxy mantiene de forma estable N conexiones paralelas y necesitas M simultáneas, el tamaño mínimo del pool es M dividido por N, redondeado hacia arriba, más un margen. Un margen del 20-30 por ciento cubre los momentos en que parte de las conexiones está ocupada con respuestas lentas.
Consejo: Siempre deja margen en el pool. Las respuestas reales son más lentas que un stub, por lo que las conexiones se mantienen durante más tiempo y la concurrencia real es mayor de lo calculado.
Estimar el tiempo de descarga
La fórmula es simple: tiempo en segundos es igual al número total de solicitudes dividido por el RPS estable. Si necesitas recopilar 1,000,000 de solicitudes con un RPS estable de 180, esto es aproximadamente 5556 segundos, es decir, alrededor de 1 hora 33 minutos sin contar pausas e reintentos.
- Toma el RPS estable del escalón de trabajo.
- Divide el volumen total de la tarea por ese RPS.
- Añade un 15-20 por ciento para reintentos de solicitudes fallidas y pausas.
Ejemplo de cálculo en pseudocódigo para mayor claridad:
total = 1000000; rps = 180; base_seg = total / rps; final_seg = base_seg * 1.2;