Las pruebas de carga no son tan demandadas y están tan extendidas como otros tipos de pruebas: no hay tantas herramientas que le permitan realizar dichas pruebas, pero generalmente se puede contar con los dedos de una mano simple y conveniente.
Cuando se trata de pruebas de rendimiento, en primer lugar, todos piensan en JMeter, sin duda sigue siendo la herramienta más famosa con la mayor cantidad de complementos. Nunca me gustó JMeter debido a su interfaz no obvia y su alto umbral de entrada, tan pronto como sea necesario probar una aplicación que no sea Hello World.
Y ahora, inspirado por el éxito de las pruebas en dos proyectos diferentes, decidí compartir información sobre un software relativamente simple y conveniente:
LocustPara aquellos que son demasiado flojos para pasar por debajo del corte, grabé un video:
Que es esto
Herramienta de código abierto que le permite especificar escenarios de carga con código Python que admite carga distribuida y, como afirman los autores, se utilizó para las pruebas de estrés de Battlelog para la serie de juegos Battlefild (inmediatamente cautivador)
De los profesionales:
- documentación simple que incluye ejemplo de copiar y pegar. Puede comenzar a realizar pruebas, incluso con casi ninguna habilidad de programación.
- "Under the hood" utiliza la biblioteca de solicitudes (HTTP para personas). Su documentación se puede utilizar como una hoja de trucos extendida y pruebas de débito
- Soporte de Python: me gusta el idioma
- El párrafo anterior ofrece multiplataforma para ejecutar pruebas
- Propio servidor web Flask para mostrar los resultados de la prueba
De las desventajas:
- Sin captura y reproducción: todas las manos
- El resultado del párrafo anterior: necesitas un cerebro. Al igual que con Postman, debe comprender cómo funciona HTTP.
- Habilidades mínimas de programación necesarias
- Modelo de carga lineal, que inmediatamente molesta a los fanáticos para generar usuarios "según Gauss"
Proceso de prueba
Cualquier prueba es una tarea compleja que requiere planificación, preparación, monitoreo de implementación y análisis de resultados. Durante la prueba de esfuerzo, si es posible, es posible y necesario recopilar todos los datos posibles que pueden afectar el resultado:
- Hardware del servidor (CPU, RAM, ROM)
- Software del servidor (SO, versión del servidor, JAVA, .NET, etc., la base de datos y la cantidad de datos en sí, el servidor y los registros de la aplicación de prueba)
- Ancho de banda de red
- La presencia de servidores proxy, equilibradores de carga y protección DDOS
- Datos de prueba de carga (número de usuarios, tiempo de respuesta promedio, número de solicitudes por segundo)
Los ejemplos descritos a continuación se pueden clasificar como pruebas de carga funcional de caja negra. Incluso sin saber nada sobre la aplicación bajo prueba y sin acceso a los registros, podemos medir su rendimiento.
Antes de empezar
Para probar las pruebas de carga en la práctica, implementé un servidor web localmente simple
https://github.com/typicode/json-server . Daré casi todos los siguientes ejemplos para él. Tomé los datos para el servidor de un ejemplo en línea implementado:
https://jsonplaceholder.typicode.com/Para ejecutarlo, se requiere nodeJS.
Spoiler obvio : al igual que con las pruebas de seguridad, es mejor realizar experimentos con pruebas de estrés en gatos localmente, sin cargar los servicios en línea para que no esté prohibido
Para comenzar, también se requiere Python: en todos los ejemplos utilizaré la versión 3.6, así como la propia langosta (al momento de escribir, versión 0.9.0). Se puede instalar con el comando
python -m pip install locustio
Los detalles de instalación se pueden encontrar en la documentación oficial.
Analizando un ejemplo
Luego necesitamos un archivo de prueba. Tomé un ejemplo de la documentación, ya que es muy simple y directo:
from locust import HttpLocust, TaskSet def login(l): l.client.post("/login", {"username":"ellen_key", "password":"education"}) def logout(l): l.client.post("/logout", {"username":"ellen_key", "password":"education"}) def index(l): l.client.get("/") def profile(l): l.client.get("/profile") class UserBehavior(TaskSet): tasks = {index: 2, profile: 1} def on_start(self): login(self) def on_stop(self): logout(self) class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000
Eso es todo! ¡Esto es realmente suficiente para comenzar la prueba! Veamos un ejemplo antes de comenzar.
Saltando importaciones, al principio vemos 2 funciones de inicio y cierre de sesión casi idénticas, que consisten en una línea.
l.client : un objeto de sesión HTTP con el que crearemos una carga. Utilizamos el método POST, que es casi idéntico al de la biblioteca de solicitudes. Casi, porque en este ejemplo estamos pasando como primer argumento no una URL completa, sino solo una parte de ella, un servicio específico.
El segundo argumento pasa los datos, y no puedo evitar notar que es muy conveniente usar los diccionarios de Python, que se convierten automáticamente a json
También puede tener en cuenta que no procesamos el resultado de la solicitud de ninguna manera; si tiene éxito, los resultados (por ejemplo, cookies) se guardarán en esta sesión. Si se produce un error, se registrará y se agregará a las estadísticas sobre la carga.
Si queremos saber si hemos escrito la solicitud correctamente, siempre podemos verificarla de la siguiente manera:
import requests as r response=r.post(base_url+"/login",{"username":"ellen_key","password":"education"}) print(response.status_code)
Agregué solo la variable
base_url , que debería contener la dirección completa del recurso que se está probando.
Las siguientes funciones son consultas, que crearán una carga. Nuevamente, no necesitamos procesar la respuesta del servidor; los resultados irán inmediatamente a las estadísticas.
Lo siguiente es la clase
UserBehavior (el nombre de la clase puede ser cualquiera). Como su nombre lo indica, describirá el comportamiento de un usuario esférico en el vacío de la aplicación bajo prueba. Pasamos a la propiedad de
tareas un diccionario de métodos que el usuario llamará y su frecuencia de llamadas. Ahora, a pesar de que no sabemos qué función y en qué orden llamará cada usuario, se seleccionan al azar, garantizamos que la función de
índice se llamará en promedio 2 veces más a menudo que la función de
perfil .
Además del comportamiento, la clase principal TaskSet le permite especificar 4 funciones que se pueden realizar antes y después de las pruebas. El orden de las llamadas será el siguiente:
- setup - se llama 1 vez al inicio de UserBehavior (TaskSet) - no está en el ejemplo
- on_start : llamado 1 vez por cada nuevo usuario de la carga al inicio
- tareas : ejecución de las tareas mismas
- on_stop : llamado una vez por cada usuario cuando la prueba termina de funcionar
- desmontaje - se llama 1 vez cuando se cierra el TaskSet - tampoco está en el ejemplo
Vale la pena mencionar aquí que hay 2 formas de declarar el comportamiento del usuario: la primera ya está indicada en el ejemplo anterior: las funciones se anuncian por adelantado. La segunda forma es declarar métodos directamente dentro de la clase
UserBehavior :
from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): def on_start(self): self.client.post("/login", {"username":"ellen_key", "password":"education"}) def on_stop(self): self.client.post("/logout", {"username":"ellen_key", "password":"education"}) @task(2) def index(self): self.client.get("/") @task(1) def profile(self): self.client.get("/profile") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000
En este ejemplo, las funciones del usuario y la frecuencia de su llamada se configuran mediante la anotación de la
tarea . Funcionalmente, nada ha cambiado.
La última clase del ejemplo es
WebsiteUser (el nombre de la clase puede ser cualquiera). En esta clase, definimos el modelo de comportamiento del usuario
UserBehavior *** +, así como los tiempos de espera mínimo y máximo entre llamadas a tareas individuales por parte de cada usuario. Para que quede más claro, aquí está cómo visualizarlo:

Empezando
Ejecute el servidor, cuyo rendimiento probaremos:
json-server --watch sample_server/db.json
También modificamos el archivo de ejemplo para que pueda probar el servicio, eliminar el inicio y cierre de sesión, establecer el comportamiento del usuario:
- Abra la página principal 1 vez al comienzo del trabajo.
- Obtenga una lista de todas las publicaciones x2
- Escribe un comentario en la primera publicación x1
from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): def on_start(self): self.client.get("/") @task(2) def posts(self): self.client.get("/posts") @task(1) def comment(self): data = { "postId": 1, "name": "my comment", "email": "test@user.habr", "body": "Author is cool. Some text. Hello world!" } self.client.post("/comments", data) class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 1000 max_wait = 2000
Para comenzar en el símbolo del sistema, ejecute el comando
locust -f my_locust_file.py --host=http://localhost:3000
donde host es la dirección del recurso probado. A él se agregarán las direcciones de los servicios indicados en la prueba.
Si no hay errores en la prueba, el servidor de carga se iniciará y estará disponible en
http: // localhost: 8089 /
Como puede ver, el servidor que vamos a probar se indica aquí: es a esta URL donde se agregarán las direcciones de los servicios del archivo de prueba.
También aquí podemos indicar el número de usuarios para la carga y su crecimiento por segundo.
En el botón, comenzamos la carga!

Resultados
Después de cierto tiempo, detenemos la prueba y echamos un vistazo a los primeros resultados:
- Como se esperaba, cada uno de los 10 usuarios creados al principio fue a la página principal
- La lista de publicaciones en promedio se abrió 2 veces más de lo que se escribió un comentario
- Hay un tiempo de respuesta promedio y medio para cada operación, el número de operaciones por segundo ya son datos útiles, aunque ahora los tome y compare con el resultado esperado de los requisitos.
En la segunda pestaña, puede ver los gráficos de carga en tiempo real. Si el servidor se bloquea con una carga determinada o su comportamiento cambia, esto será visible inmediatamente en el gráfico.

En la tercera pestaña, puede ver los errores; en mi caso, este es un error del cliente. Pero si el servidor devuelve un error 4XX o 5XX, su texto se escribirá aquí
Si se produce un error en el código de su texto, caerá en la pestaña Excepciones. Hasta ahora, tengo el error más común relacionado con el uso del comando print () en el código; esta no es la mejor manera de iniciar sesión :)
En la última pestaña, puede descargar todos los resultados de la prueba en formato csv
¿Son relevantes estos resultados? Vamos a resolverlo. La mayoría de las veces, los requisitos de rendimiento (si los hay) suenan así: el tiempo promedio de carga de la página (respuesta del servidor) debe ser inferior a N segundos con una carga de M usuarios. Realmente no especifica qué deben hacer los usuarios. Y me gusta la langosta por esto: crea actividad de un número específico de usuarios que realizan aleatoriamente las acciones esperadas que esperan de los usuarios.
Si necesitamos llevar a cabo un punto de referencia, para medir el comportamiento del sistema con diferentes cargas, podemos crear varias clases de comportamiento y realizar varias pruebas con diferentes cargas.
Eso es suficiente para empezar. Si te gustó el artículo, planeo escribir sobre:
- escenarios de prueba complejos en los que los resultados de un paso se utilizan en los siguientes
- procesamiento de respuesta del servidor, como puede estar equivocado incluso si HTTP 200 OK ha llegado
- dificultades obvias que pueden encontrarse y cómo sortearlas
- prueba sin usar la interfaz de usuario
- prueba de carga distribuida