Привет! Вы когда-нибудь задумывались, насколько ваш веб-сервис способен выдержать шквал запросов? Что произойдёт, если ваши пользователи, словно зомби, хлынут на сервер тысячами? Вот тут-то и начинается история про нагрузочное тестирование, которое помогает понять, где у вашего приложения «узкие места». А инструмент, о котором мы сегодня поговорим, — Gatling. Это мощная, элегантная и, честно говоря, недооценённая альтернатива монстрам вроде JMeter, LoadRunner и k6. Но давайте разбираться по порядку.
Что такое Gatling? И зачем он нужен?
Представьте, что ваш сервер — это кафе. Клиенты приходят, заказывают капучино, занимают столики и ожидают обслуживания. Всё идёт гладко, пока посетителей немного. Но вот, внезапно, толпа людей устремляется в ваше уютное кафе. Могут ли ваши бариста справиться? Хватит ли столиков? Не придётся ли кому-то пить кофе на улице? Вот именно эти вопросы Gatling помогает решить для вашего веб-приложения. Он проверяет, как сервер поведёт себя при нагрузке, начиная с нескольких пользователей и заканчивая тысячами.
Gatling — это современный инструмент для нагрузочного тестирования. Он написан на Scala, но не пугайтесь — сценарии можно писать и на Java или Kotlin, что особенно удобно для пользователей, привыкших к этому языку. Благодаря совместимости с Maven или Gradle, интеграция Gatling в ваш проект становится лёгкой и удобной.
Поскольку Gatling использует архитектуру Akka, управляемую сообщениями, он может запускать тысячи виртуальных пользователей на одной машине. Это связано с тем, что Akka отменяет ограничение JVM на обработку большого количества потоков. Виртуальные пользователи в Gatling — это сообщения, а не потоки.
Почему Gatling? А что с другими?
На рынке много инструментов для нагрузочного тестирования, и каждый из них имеет свои особенности. Но у Gatling есть преимущества, которые выделяют его на фоне конкурентов.
1. Gatling против JMeter
JMeter — один из самых популярных инструментов, но он далеко не идеален:
-
Тяжелый GUI: Для создания тестов приходится использовать графический интерфейс, что неудобно для версионирования и автоматизации.
-
Высокое потребление ресурсов: JMeter создаёт отдельный поток для каждого виртуального пользователя, что значительно нагружает систему.
-
Сложность работы с параметризацией: Работа с внешними данными требует дополнительных настроек и не всегда интуитивно понятна.
Преимущества Gatling:
-
Лёгкие и читаемые сценарии на Java или Scala.
-
Низкое потребление ресурсов благодаря асинхронной архитектуре.
-
Простая параметризация через CSV, JSON или другие источники данных.
2. Gatling против LoadRunner
LoadRunner — корпоративный гигант с мощным функционалом, но:
-
Высокая стоимость: Это коммерческий инструмент с дорогими лицензиями.
-
Сложность настройки: Для начальной настройки LoadRunner требуется больше времени.
-
Устаревший подход: Скрипты LoadRunner выглядят громоздко и требуют специфических знаний.
Преимущества Gatling:
-
Открытый исходный код и бесплатность.
-
Интеграция в CI/CD: запуск тестов легко автоматизировать.
-
Современные отчёты с подробной аналитикой.
3. Gatling против k6
k6 — ещё один популярный инструмент, который часто сравнивают с Gatling. Основные минусы k6:
-
Язык сценариев: k6 использует JavaScript, который не всем удобен для сложных тестов.
-
Ограниченный функционал для отчётов: Отчёты менее детализированы по сравнению с Gatling.
-
Проблемы с параметризацией: Работа с большими данными требует дополнительных усилий.
Преимущества Gatling:
-
Гибкий DSL для описания сценариев.
-
Более мощные и информативные отчёты.
-
Возможность интеграции с Java-проектами через Maven или Gradle.
Как это работает? Быстрое погружение в сценарии
Для работы с Gatling с использованием языка программирования Java необходимо выбрать одну из IDEA и сборщик Maven или Gardle. Я в качестве примера буду использовать IntellijIDEA и Maven.
Для начала необходимо создать новый проект с произвольным названием.
Если же вы создали обычный проект Maven с выбором стандартного Archetype то дополнительно можно подтянуть нужные зависимости и плагин в pom.xml
<dependencies>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>${gatling.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</plugin>
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>${gatling-maven-plugin.version}</version>
</plugin>
</plugins>
</build>
Теперь давайте посмотрим на простой сценарий в Gatling, чтобы понять, как он работает.
Настройка протокола
Для начала нужно настроить протокол — это как задать маршрут для бариста, чтобы они знали, куда подавать кофе.
HttpProtocolBuilder httpProtocol = http
.baseUrl("https://example.com") // Ваш сервер
.acceptHeader("application/json");
Создание сценария
Сценарий — это план действий виртуального пользователя. Например:
ScenarioBuilder scn = scenario("Basic Scenario")
.exec(http("Get Home Page")
.get("/")
.check(status().is(200)))
.pause(5) // пауза 5 секунд
.exec(http("Search Query")
.get("/search?q=gatling")
.check(status().is(200)));
Каждый .exec()
— это шаг. Здесь мы:
-
Делаем запрос на главную страницу.
-
Ждём 5 секунд (имитация реального поведения).
-
Выполняем поиск.
Методы Gatling: детально о каждом шаге
Теперь рассмотрим ключевые методы Gatling, которые используются в скриптах:
-
exec()
: Выполняет действия. Это основной метод для отправки запросов или выполнения пользовательских действий.
exec(http("Get Home Page").get("/").check(status().is(200)));
Внутри exec
можно передавать запросы или проверки, которые сервер должен пройти.
-
pause()
: Добавляет паузу между действиями. Полезно для имитации реального поведения пользователей.
pause(2); // Пауза 2 секунды
pause(1, 5); // Пауза от 1 до 5 секунд
-
repeat()
: Повторяет определённое действие несколько раз.
repeat(10).on(
exec(http("Repeated Request").get("/repeat"))
);
-
feed()
: Используется для подачи данных из внешнего источника, например CSV или JSON.
Iterator<Map<String, Object>> feeder = csv("users.csv").readRecords();
scn.feed(feeder);
-
group()
: Группирует действия для отображения в отчёте.
group("User Actions").on(
exec(http("Login").post("/login")),
exec(http("Get Data").get("/data"))
);
Настройка нагрузки
Когда сценарий готов, вы задаёте нагрузку. Например:
setUp(
scn.injectOpen(rampUsers(50).during(30)) // Постепенно добавляем 50 пользователей за 30 секунд
).protocols(httpProtocol);
Дополнительные методы
doIf()
и doIfOrElse()
Позволяют выполнять действия на основе условий.
scn.exec(session -> session.set("flag", true)) // Устанавливаем флаг
.doIf(session -> session.getBoolean("flag")).then(
exec(http("Condition Met").get("/condition"))
);
-
doIf()
: Выполняет блок кода, если условие истинно. -
doIfOrElse()
: Позволяет задать альтернативный блок действий.
tryMax()
Позволяет повторить шаг определённое количество раз при неудаче (например, если HTTP-запрос возвращает ошибку).
tryMax(3).on(
exec(http("Retry Request").get("/unstable-endpoint"))
);
В этом примере запрос будет повторён максимум 3 раза, если произойдёт ошибка.
foreach()
Циклически обрабатывает элементы коллекции.
List<String> items = Arrays.asList("item1", "item2", "item3");
scn.foreach(items, "item").on(
exec(http("Get Item").get("/items/${item}"))
);
-
items
: Коллекция, элементы которой будут обрабатываться. -
${item}
: Переменная для текущего элемента в цикле.
exitHereIf()
Позволяет завершить сценарий для конкретного пользователя, если выполняется определённое условие.
scn.exec(http("Check Status")
.get("/status")
.check(status().is(500)))
.exitHereIf(session -> session.contains("error"));
Если сессия содержит ошибку, выполнение сценария для пользователя будет прервано.
rendezVous()
Используется для синхронизации пользователей в сценарии. Например, вы можете заставить группу пользователей начать следующий шаг одновременно.
scn.rendezVous(10) // Ждёт, пока 10 пользователей достигнут этой точки
.exec(http("Sync Step").get("/sync"));
during()
Позволяет выполнять блок действий в течение заданного периода времени.
scn.during(30).on( // Выполняет в течение 30 секунд
exec(http("Ping Server").get("/ping"))
);
randomSwitch()
Позволяет случайным образом выбирать блоки действий с определённой вероятностью.
scn.randomSwitch()
.on(
70.0, exec(http("70% Path").get("/path1")),
30.0, exec(http("30% Path").get("/path2"))
);
-
70.0 и 30.0: Процент вероятности выполнения блока.
Далее из консоли вводим стандартную команду mvn clean install
для сборки проекта и далее mvn gatling:test
для запуска Gatling сценария.
И всё! Вы запускаете тест и смотрите, как сервер справляется.
Всю представленную информацию так же можно найти на официальном сайте
Документация Gatling
Автор: RomanPers