Одним из критически важных этапов разработки веб-приложений является нагрузочное тестирование. Этот процесс необходим для выявления потенциальных узких мест в системе, а также для обеспечения того, что приложение способно выдерживать предполагаемое количество пользователей и запросов без ухудшения производительности.

Введение в нагрузочное тестирование

Нагрузочное тестирование представляет собой процесс имитации различных уровней пользовательской активности с целью оценки производительности веб-приложения. Основная задача такого тестирования — определить, насколько эффективно система справляется с возрастающим трафиком и где находятся ее пределы. В процессе нагрузочного тестирования выявляются критические точки отказа и оценивается максимальная пропускная способность приложения.

Основные методики нагрузочного тестирования

Пиковая нагрузка (Spike Testing): Данный метод фокусируется на оценке поведения системы при резком увеличении нагрузки. Он позволяет определить, насколько стабильно приложение работает при внезапных всплесках пользовательской активности.

Длительное тестирование (Soak Testing): Этот метод используется для проверки устойчивости системы при стабильной нагрузке на протяжении продолжительного времени. Он помогает выявить долгосрочные проблемы, такие как утечки памяти или сбои в работе базы данных.

Стресс-тестирование (Stress Testing): Этот тип тестирования оценивает производительность системы при нагрузке, превышающей ожидаемую. Цель состоит в том, чтобы определить точку, при которой приложение начинает сбоить, и как быстро оно восстанавливается после перегрузки.

Постепенное увеличение нагрузки (Ramp-Up Testing): В рамках этой методики нагрузка на систему увеличивается постепенно до достижения определенного уровня. Это позволяет выявить пороговые значения, при которых начинаются проблемы с производительностью.

Инструменты для нагрузочного тестирования Java веб-приложений

Для эффективного нагрузочного тестирования Java веб-приложений существует множество инструментов, каждый из которых обладает своими особенностями и преимуществами. Ниже представлены наиболее востребованные из них:

Apache JMeter: Один из наиболее популярных инструментов для проведения нагрузочного тестирования, поддерживающий различные протоколы, включая HTTP, HTTPS, SOAP и JDBC. JMeter предоставляет широкий функционал для создания сложных тестовых сценариев и последующего анализа результатов. Универсальность и мощные аналитические возможности делают его предпочтительным решением для нагрузочного тестирования веб-приложений.

# Пример запуска теста в JMeter
jmeter -n -t test_plan.jmx -l results.jtl -e -o /path/to/output/folder

Gatling: Этот инструмент, разработанный на языке Scala, известен своей высокой производительностью и возможностью масштабирования тестов. Gatling предлагает удобный DSL (Domain-Specific Language) для создания сценариев тестирования и обладает мощными средствами анализа, что делает его идеальным для проведения сложных и ресурсоемких нагрузочных тестов.

class BasicSimulation extends Simulation {
  val httpProtocol = http.baseUrl("http://localhost:8080")
  val scn = scenario("Basic Scenario")
    .exec(http("request_1").get("/"))
  setUp(scn.inject(atOnceUsers(1000)).protocols(httpProtocol))
}

Locust: Инструмент для нагрузочного тестирования, написанный на Python. Locust позволяет разрабатывать тестовые сценарии на языке Python, что делает его особенно привлекательным для разработчиков, уже знакомых с этим языком. Он легко интегрируется с другими системами и предлагает гибкие возможности управления нагрузкой, что делает его подходящим для различных типов проектов.

from locust import HttpUser, TaskSet, task

class UserBehavior(TaskSet):
    @task
    def index(self):
        self.client.get("/")

class WebsiteUser(HttpUser):
    tasks = [UserBehavior]
    min_wait = 5000
    max_wait = 9000

Apache Benchmark (ab): Это легковесный инструмент, предназначенный для быстрого выполнения базовых тестов производительности веб-серверов. Apache Benchmark отличается простотой использования и подходит для быстрой оценки производительности веб-приложений в условиях базовой нагрузки. Однако, его функционал ограничен, и он не предназначен для выполнения сложных сценариев тестирования.

# Пример запуска теста с 1000 запросами и 100 параллельными пользователями
ab -n 1000 -c 100 http://localhost:8080/

Пример нагрузочного тестирования с использованием Apache JMeter

Ниже приводится подробный пример выполнения нагрузочного тестирования Java веб-приложения с использованием Apache JMeter. Этот пример охватывает все ключевые этапы: от установки JMeter до анализа результатов тестирования.

Установка Apache JMeter

  1. Загрузка и установка: Загрузите последнюю версию Apache JMeter с официального сайта.

  2. Запуск JMeter: После установки запустите JMeter с помощью команды jmeter в командной строке.

Создание тестового плана

  1. Создание нового тестового плана: Откройте JMeter и создайте новый тестовый план.

  2. Добавление группы потоков (Thread Group): Группа потоков определяет количество пользователей, время разгона (Ramp-Up) и количество циклов выполнения теста.

ThreadGroup threadGroup = new ThreadGroup();
threadGroup.setName("Example Thread Group");
threadGroup.setNumThreads(100);  // Указание числа пользователей (потоков)
threadGroup.setRampUp(10);       // Время разгона в секундах
threadGroup.setLoopCount(1);     // Количество циклов

Добавление элементов к тестовому плану

HTTP Request Sampler: Добавьте HTTP Request Sampler для задания HTTP-запросов к тестируемому веб-приложению. Укажите домен, порт, путь и метод запроса.

HTTPSampler httpSampler = new HTTPSampler();
httpSampler.setDomain("example.com");
httpSampler.setPort(80);
httpSampler.setPath("/api/test");
httpSampler.setMethod("GET");

Конфигурация тестового плана: Добавьте настроенную группу потоков и HTTP Request Sampler в тестовый план.

TestPlan testPlan = new TestPlan("Example Test Plan");
testPlan.addThreadGroup(threadGroup);
threadGroup.addSampler(httpSampler);

Добавление слушателей для анализа результатов

View Results Tree: Этот элемент позволяет визуализировать результаты выполнения каждого запроса в виде дерева.

ViewResultsTree resultsTree = new ViewResultsTree();
testPlan.add(resultsTree);

Summary Report: Предоставляет сводные статистические данные о результатах тестирования.

SummaryReport summaryReport = new SummaryReport();
testPlan.add(summaryReport);

Запуск теста и анализ результатов

Запуск теста: Настройте и запустите тестовый план с помощью движка JMeter.

JMeterEngine jmeterEngine = new StandardJMeterEngine();
jmeterEngine.configure(testPlan);
jmeterEngine.run();

Анализ результатов: После завершения теста проанализируйте результаты для выявления узких мест и потенциальных точек отказа.

for (SampleResult result : resultsTree.getResults()) {
    System.out.println("Response time: " + result.getTime());          // Время ответа
    System.out.println("Response code: " + result.getResponseCode());  // Код ответа сервера
}

Этот пример демонстрирует, как можно использовать Apache JMeter для создания и выполнения нагрузочного тестирования веб-приложений на Java, а также для анализа результатов тестирования с целью улучшения производительности и надежности системы.

Преобразование нагрузки в RPS (Запросы в секунду) в Apache JMeter

Когда нагрузка на систему должна выражаться в запросах в секунду (RPS) вместо количества пользователей, подход к настройке тестового плана в Apache JMeter требует некоторых изменений. Это особенно полезно, когда нагрузка на различные адреса может отличаться при параллельном выполнении запросов. Рассмотрим пошаговую инструкцию по настройке такого теста.

Шаг 1: Настройка переменной load_msg_sec

  1. Добавление переменной в тестовый план:

    • Кликните на корневой элемент тестового плана в JMeter.

    • Добавьте новую переменную с именем load_msg_sec. Название переменной можно выбрать любое, но для ясности будем использовать load_msg_sec.

  2. Установка значения переменной:

    • В значении переменной можно указать как константное значение, так и параметризованное. Для параметризованного значения в столбце Value введите следующее:

${__P(load, 50)}

Здесь load — это название параметра, а 50 — значение по умолчанию, если параметр не передан.

Шаг 2: Добавление Constant Throughput Timer

  1. Добавление таймера:

    • Перейдите в вашу группу потоков (Thread Group).

    • Добавьте Constant Throughput Timer: RBMAddTimerConstant Throughput Timer.

  2. Настройка таймера:

    • В поле Target throughput (in samples per minute) укажите следующую формулу:

${__jexl3(${load_msg_sec} * 60 * #percent#)}

Здесь:

  • ${load_msg_sec} — количество запросов в секунду, определённое переменной load_msg_sec.

  • 60 — коэффициент для перевода запросов в секунду в запросы в минуту (так как JMeter ожидает значения в запросах в минуту).

  • #percent# — процент от общего количества запросов, который будет применяться при параллельном тестировании нескольких адресов. Этот параметр определяется согласно методике нагрузочного тестирования.

Шаг 3: Настройка Basic Authentication

При тестировании на стендах, защищённых базовой аутентификацией (Basic Auth), необходимо настроить корректную отправку заголовка авторизации, чтобы избежать ошибок 403.

  1. Добавление BeanShell PreProcessor:

    • Кликните на вашу группу потоков (Thread Group).

    • Добавьте BeanShell PreProcessor: RBMAddPre ProcessorsBeanShell PreProcessor.

    • Вставьте следующий код в BeanShell PreProcessor:

    import org.apache.commons.codec.binary.Base64;
    byte[] encodedUsernamePassword = Base64.encodeBase64("login:password".getBytes());
    vars.put("base64HeaderValue", new String(encodedUsernamePassword));
    
    • Здесь "login:password" замените на реальные данные для аутентификации.

  2. Добавление HTTP Header Manager:

    • В той же группе потоков добавьте HTTP Header Manager.

    • Добавьте следующий заголовок:

      • Name: Authorization

      • Value: Basic ${base64HeaderValue}

    • Переменная ${base64HeaderValue}, используемая в значении заголовка, была установлена в BeanShell PreProcessor.

Лучшие практики нагрузочного тестирования

  1. Реалистичные сценарии: Тестовые сценарии должны максимально приближаться к реальным условиям эксплуатации приложения. Это позволяет точно моделировать нагрузки и предсказывать поведение системы, выявляя потенциальные проблемы до того, как они появятся в реальной эксплуатации.

  2. Мониторинг и анализ: Эффективное нагрузочное тестирование требует тщательного мониторинга системных ресурсов и анализа полученных данных. Использование специализированных инструментов позволяет оперативно выявлять узкие места и принимать меры для их устранения, тем самым оптимизируя производительность системы.

  3. Регулярность тестирования: Постоянное проведение нагрузочных тестов необходимо для своевременного выявления проблем, особенно после внесения изменений в код или инфраструктуру. Регулярное тестирование помогает поддерживать высокую стабильность и производительность приложения.

  4. Интеграция с CI/CD: Включение нагрузочного тестирования в процессы непрерывной интеграции и доставки (CI/CD) обеспечивает автоматический контроль производительности после каждого изменения кода. Это позволяет минимизировать риск появления проблем с производительностью в продакшене.

  5. Комбинирование инструментов: Использование различных инструментов для нагрузочного тестирования (например, Apache JMeter, Gatling, Locust) помогает получить всесторонний обзор производительности приложения. Это позволяет выявлять разные аспекты поведения системы под нагрузкой и разрабатывать более комплексные решения для их оптимизации.

  6. Многоуровневое тестирование: Проведение тестов при различных уровнях нагрузки — от минимальной до экстремальной — позволяет глубже понять поведение приложения и определить его пределы. Это помогает выявить, как система реагирует на разные сценарии нагрузки и где могут возникнуть проблемы.

  7. Документирование и отчеты: Ведение подробной документации и создание отчетов по результатам тестирования важны для анализа и последующего улучшения системы. Это позволяет отслеживать тенденции, делать обоснованные выводы и планировать дальнейшие шаги по оптимизации.

  8. Оптимизация и настройка: Результаты нагрузочного тестирования должны быть использованы для оптимизации системы. Это может включать настройку серверов, оптимизацию запросов к базе данных, улучшение алгоритмов и другие меры, направленные на повышение производительности приложения.

  9. Сотрудничество с командой: Эффективное тестирование требует тесного взаимодействия с разработчиками и другими заинтересованными сторонами. Совместная работа позволяет быстро реагировать на обнаруженные проблемы и вносить улучшения в продукт.

Пример протокола нагрузочного тестирования веб-приложения

Документ описывает результаты проведения нагрузочного тестирования системы.

1. Объект испытаний

Объектом испытаний является веб-приложение, которое подвергалось нагрузочному тестированию с целью оценки его производительности и стабильности при различных уровнях нагрузки.

2. Цели испытаний

  • Проверка соответствия разработанной системы предъявляемым требованиям.

  • Определение максимальной и пиковой производительности системы.

  • Проверка стабильности работы системы в течение длительного времени под нагрузкой.

3. Базовый профиль

Базовый профиль тестирования включает в себя следующие параметры, рассчитанные на основе требований к производительности и предполагаемого пользовательского поведения:

Код

Требование

Значение

1

Общее количество запросов к системе в месяц

30 000 000

2

Пиковое количество запросов в месяц

3 300 000

3

Пиковое количество запросов в неделю

900 000

4

Пиковое количество запросов в день

110 000

5

Количество пользователей в месяц

300 000

6

Количество пользователей в неделю

60 000

7

Количество пользователей в день

10 000

Расчетное количество запросов в час на основе максимального количества запросов:

На основе Customer Journey Map (CJM) выделены объекты, создающие основную нагрузку:

Объект

URI

Процент запросов в час

Количество запросов в час

Объект 1

/

27%

11 340

Объект 2

/…/

15%

6 300

Объект 3

/…/

11%

4 620

Объект 4

/…/

5%

2 100

Объект 5

/…/

16%

6 720

Объект 6

/…/

5%

2 100

Объект 7

/…/

6%

2 520

Объект 8

/…/

15%

6 300

Нагрузку на объекты распределили пропорционально целевой аудитории. Запросы распределяются равномерно в течение часа:

4. План тестирования

  1. Разработка профиля нагрузочного тестирования.

  2. Настройка систем мониторинга для отслеживания производительности.

  3. Разработка скриптов генерации нагрузки.

  4. Подготовка тестовых данных (например, варианты значений для поисковых запросов) и создание скриптов для их загрузки в базу данных перед тестированием и очистки после.

  5. Проведение нагрузочных тестов.

  6. Анализ результатов нагрузочного тестирования.

5. Типы проводимых тестов

Поиск пиковой производительности

  • Нагрузка: ступенчатая.

  • Начальная нагрузка: 40% от базового профиля.

  • Шаг: увеличение на 30% от базового профиля.

  • Продолжительность ступени: 30 минут или до отказа системы.

  • Критерий завершения теста: отказ системы.

  • Критерий успешности: максимальная производительность системы превышает значения базового профиля.

Проверка стабильности работы системы

  • Нагрузка: равномерная с плавным разгоном.

  • Максимальная нагрузка: базовый профиль.

  • Время разгона: 15 минут.

  • Длительность основного этапа: 4 часа.

  • Критерий завершения теста: истечение времени или отказ системы.

  • Критерий успешности: завершение теста по истечении времени без отказов системы.

Требования к производительности системы

1. Утилизация ресурсов во время тестов

Для достижения устойчивой производительности и стабильности системы под нагрузкой утилизация ресурсов сервера во время тестирования должна соответствовать следующим показателям:

  • Утилизация CPU: не более 80%.

  • Утилизация RAM: не более 80%.

Эти требования были установлены на основании экспертной оценки и служат ориентиром, поскольку специфические требования к системе не были предъявлены.

2. Требования к количеству неуспешных запросов

Для обеспечения надежности системы количество неуспешных запросов не должно превышать следующие показатели, в зависимости от интенсивности входящей нагрузки:

  • Тест поиска максимальной производительности (последняя ступень) — не более 0,1% от общего числа запросов.

  • Тест подтверждения максимальной производительности (в течение всего теста) — не более 0,1%.

  • Тест стабильности (в течение всего теста) — не более 0,1%.

Периоды агрегации результатов соответствуют периодам постоянной нагрузки в каждом из тестов.

3. Требования ко времени отклика и интенсивности типовых шагов бизнес-операций

Для обеспечения высокой производительности и быстрого отклика система должна удовлетворять следующим требованиям по времени отклика при выполнении типовых шагов бизнес-операций:

  • 90% запросов должны обрабатываться за время не более 50 мс.

  • 99% запросов должны обрабатываться за время не более 1000 мс.

  • 99,9% запросов должны обрабатываться за время не более 2500 мс.

Эти требования обеспечивают предсказуемое поведение системы под нагрузкой и минимизируют вероятность задержек, критичных для бизнес-процессов.

Конфигурация стенда для Нагрузочного Тестирования (НТ)

Компонент

Значение

CPU Cores

XX

RAM

XXX GB

Поиск максимальной производительности

Максимальная производительность системы определяется как нагрузка, при которой утилизация ресурсов (CPU и RAM) начинает превышать допустимые границы, указанные в разделе "Требования по утилизации ресурсов". Поиск максимальной производительности проводится через серию тестов с пошаговым увеличением нагрузки относительно базового профиля.

Шаг увеличения нагрузки:
Шаг рассчитывается по формуле:
Шаг = Значение базового профиля * 0,4 = 4.8 ≈ 5 запросов в секунду

Продолжительность каждого шага составляет 30 минут.

Результаты тестов:

Шаг

RPS

CPU

RAM

Среднее время отклика (ms)

Минимальное время отклика (ms)

Максимальное время отклика (ms)

Ошибка %

Результат

1

12 q/s

31,5%

6%

853

342

2332

0,01%

Соответствует

2

17 q/s

44,8%

6%

823

339

3132

0,00%

Соответствует

3

22 q/s

58,6%

6%

851

353

2041

0,00%

Соответствует

4

27 q/s

73,6%

7%

841

337

2652

0,00%

Соответствует

5

32 q/s

88,0%

7%

764

342

2332

0,00%

CPU превышает допустимый порог, производительность максимальна

Графики:

Шаг 1

График результатов теста из jMeter
График результатов теста из jMeter
График использования ЦПУ (Grafana)
График использования ЦПУ (Grafana)
График использования ОЗУ
График использования ОЗУ

Шаг 2

График результатов теста из jMeter
График результатов теста из jMeter
График использования ЦПУ (Grafana).png
График использования ЦПУ (Grafana)
График использования ОЗУ.png
График использования ОЗУ

Шаг 3

График результатов теста из jMeter.png
График результатов теста из jMeter
График использования ЦПУ (Grafana).png
График использования ЦПУ (Grafana)
График использования ОЗУ.jpg
График использования ОЗУ

Шаг 4

График результатов теста из jMeter.png
График результатов теста из jMeter
График использования ЦПУ (Grafana).png
График использования ЦПУ (Grafana)

Шаг 5

График результатов теста из jMeter.png
График результатов теста из jMeter
График использования ЦПУ (Grafana).png
График использования ЦПУ (Grafana)
График использования ОЗУ.png
График использования ОЗУ

Вывод: Максимальная производительность системы составляет XX запросов в секунду, что превышает ожидаемую нагрузку на XX%. Система удовлетворяет требованиям производительности.

Тест на стабильность системы

Тест на стабильность проводится при нагрузке, соответствующей базовому профилю, на протяжении длительного времени (4 часа).

Результаты теста на стабильность:

RPS

CPU

RAM

Среднее время отклика (ms)

Минимальное время отклика (ms)

Максимальное время отклика (ms)

Ошибка %

Результат

12 q/s

33,6%

7%

503

343

4116

0,00%

Соответствует

Графики:

График результатов теста из jMeter
График результатов теста из jMeter
График использования ЦПУ (Grafana)
График использования ЦПУ (Grafana)
График использования ОЗУ
График использования ОЗУ

Вывод: На протяжении 4 часов тестирования стабильности системы процент отказов составил 0%, нагрузка равномерно распределена. Система удовлетворяет описанным показателям производительности.

Комментарии (0)