- PVSM.RU - https://www.pvsm.ru -
Нагрузочное тестирование не так сильно востребовано и распространено, как иные виды тестирования — инструментов, позволяющих, провести такое тестирование, не так много а простых и удобных вообще можно пересчитать на пальцах одной руки.
Когда речь заходить о тестировании производительности — в первую очередь все думают о JMeter’е — он бесспорно остается самым известным инструментом с самым большим количеством плагинов. Мне же JMeter никогда не нравился из-за неочевидного интерфейса и высокого порога вхождения, как только возникает необходимость протестировать не Hello World приложение.
И вот, окрыленный успехом проведения тестирования в двух различных проектах, решил поделится информацией об относительно простом и удобном софте — Locust [1]
Для тех, кому лень идти под кат, записал видео:
Опенсорс тул, позволяющий задать сценарии нагрузки Python кодом, поддерживающий распределенную нагрузку и, как уверяют авторы, использовался для нагрузочного тестирования Battlelog для серии игр Battlefild (сразу подкупает)
Из плюсов:
Из минусов:
Любое тестирование — комплексная задача, требующая планирования, подготовки, контроля выполнения и анализа результатов. При нагрузочном тестировании, если есть возможность, можно и нужно собирать все возможные данные, которые могут повлиять на результат:
Описанные далее примеры можно классифицировать как black-box функциональное нагрузочное тестирование. Даже не зная ничего о тестируемом приложении и не доступа к логам, мы можем измерить его производительность.
Для того, чтобы на практике проверять нагрузочные тесты, я развернул локально простой веб сервер https://github.com/typicode/json-server [5]. Почти все следующие примеры я буду приводить для него. Данные для сервера я взял из развернутого онлайн примера — https://jsonplaceholder.typicode.com/ [6]
Для его запуска необходим nodeJS.
Очевидный спойлер: как и с тестированием безопасности — эксперименты с нагрузочным тестированием лучше проводить на кошках локально, не нагружая онлайн сервисы, чтобы вас не забанили
Для того, чтобы начать, также необходим Python — во всех примерах я буду использовать версию 3.6, а также сам locust (на момент написания статьи — версия 0.9.0). Его можно установить командой
python -m pip install locustio
Подробности установки можно подсмотреть в официальной документации.
Далее нам нужен файл теста. Я взял пример из документации, так как он очень прост и понятен:
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
Все! Этого реально достаточно, чтобы начать тест! Давайте разберем пример, прежде чем перейдем к запуску.
Пропуская импорты, в самом начале мы видим 2 почти одинаковые функции логина и логаута, состоящие из одной строчки. l.client — объект HTTP сессии, с помощью которой мы будем создавать нагрузку. Мы используем метод POST, почти идентичный такому же в библиотеке requests. Почти — потому что в данном примере мы передаем в качестве первого аргумента не полный URL, а только его часть — конкретный сервис.
Вторым аргументом передаются данные — и не могу не заметить, что очень удобно использовать словари Python, которые автоматически конвертируются в json
Также можно обратить внимание, что мы никак не обрабатываем результат запроса — если он будет успешен, результаты (например cookie), будут сохранены в этой сессии. Если произойдет ошибка — она будет записана и добавлена в статистику о нагрузке
Если нам захочется узнать, правильно ли мы написали запрос, можно всегда проверить его следующим образом:
import requests as r
response=r.post(base_url+"/login",{"username":"ellen_key","password":"education"})
print(response.status_code)
Я добавил только переменную base_url, которая должна содержать полный адрес тестируемого ресурса.
Следующее несколько функций — запросы, за счет которых будет создаваться нагрузка. Опять же, нам не нужно обрабатывать ответ сервера — результаты пойдут сразу в статистику.
Дальше — класс UserBehavior (название класса может быть любое). Как видно из названия, в нем будет описано поведение сферического пользователя в вакууме тестируемого приложения. Свойству tasks мы передаем словарь методов, которые будет вызывать пользователь и их частоту вызовов. Теперь, несмотря на то, что мы не знаем, какую функцию и в каком порядке будет вызывать каждый пользователь — они выбираются случайно, мы гарантируем, что функция index вызовется в среднем в 2 раза чаще, чем функция profile.
Кроме поведения, родительский класс TaskSet позволяет задать 4 функции, которые можно выполнить до и после тестов. Порядок вызовов будет следующим:
Здесь же стоит упомянуть, что есть 2 способа объявления поведения пользователя: первый уже указан в примере выше — функции объявлены заранее. Второй способ — объявления методов прямо внутри класса 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
В этом примере функции пользователя и частота их вызова задана с помощью аннотации task [7]. Функционально же ничего не изменилось
Последний класс из примера — WebsiteUser (название класса может быть любое). В этом классе мы задаем модель поведений пользователя UserBehavior***+, а также минимальное и максимальное время ожидания между вызовами отдельных task каждым пользователем. Чтобы было понятнее, вот как это можно визуализировать:

Запустим сервер, производительность которого мы будем тестировать:
json-server --watch sample_server/db.json
Так же модифицируем файл примера, чтобы он соответствовал мог тестировать сервис, уберем логин и логаут, зададим поведение пользователя:
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
Для запуска в командной строке надо выполнить команду
locust -f my_locust_file.py --host=http://localhost:3000
где host — адрес тестируемого ресурса. Именно к нему будут добавлены адреса сервисов, указанные в тесте.
Если никаких ошибок в тесте нет, нагрузочный сервер запустится и будет доступен по адресу http://localhost:8089/ [8]

Как видим, здесь указан сервер, который мы будем тестировать — именно к этому URL будут добавлятся адреса сервисов из файла теста.
Также здесь мы можем указать количество пользователей для нагрузки и их прирост в секунду.
По кнопке начинаем нагрузку!

Через определенное время остановим тест и взглянем на первые результаты:
На второй вкладке можно посмотреть графики нагрузки в режиме реального времени. В случае, если сервер падает при определенной нагрузке или его поведение меняется, на графике это будет сразу видно.

На третей вкладке можно посмотреть ошибки — в моем случае это ошибка клиента. Но если сервер вернет 4ХХ или 5ХХ ошибку — ее текст будет записан именно здесь
Если ошибка случится в коде вашего текста — она попадет во вкладку Exceptions. Пока что у меня самая частая ошибка связана с использованием команды print() в коде — это не лучший способ логирования :)
На последней вкладке можно загрузить все результаты теста в формате csv
Эти результаты релевантны? Давайте разберемся. Чаще всего требования к производительности (если такие вообще заявляются) звучат примерно так: среднее время загрузки страницы (ответа сервера) должно быть меньше N секунд при нагрузке M пользователей. Не особо уточняя, что должны делать пользователи. И этим мне нравится locust — он создает активность конкретного числа пользователей, которые в случайном порядке выполняют предполагаемые действия, которые ожидают от пользователей.
Если нам нужно провести benchmark — измерить поведение системы при разной нагрузке, мы может создать несколько классов поведения и провести несколько тестов при разных нагрузках.
Для начала этого достаточно. Если вам понравилась статья, я в ближайшее время планирую написать о:
Автор: Ypurek
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/299722
Ссылки в тексте:
[1] Locust: https://locust.io
[2] requests : http://docs.python-requests.org/
[3] Flask: http://flask.pocoo.org/
[4] мозг: http://www.braintools.ru
[5] https://github.com/typicode/json-server: https://github.com/typicode/json-server
[6] https://jsonplaceholder.typicode.com/: https://jsonplaceholder.typicode.com/
[7] task: https://habr.com/users/task/
[8] http://localhost:8089/: http://localhost:8089/
[9] Источник: https://habr.com/post/430502/?utm_campaign=430502
Нажмите здесь для печати.