Lesson 13 of 26

Урок 13 — Load Testing

Название: Load Testing — проверяем систему под нагрузкой
Описание: Разбираем нагрузочное тестирование: что это, зачем нужно, как проводить, какие метрики собирать и какие инструменты использовать. Примеры с k6 и JMeter.
Почему это важно для QA: Сайт может отлично работать для одного пользователя и падать при 500. Нагрузочное тестирование — это страховка от позорных падений в момент рекламной кампании или пика продаж.


1. Что такое Load Testing

Load Testing (нагрузочное тестирование) — это тип нефункционального тестирования, при котором система проверяется под ожидаемой нагрузкой — тем количеством пользователей или запросов, которое реально ожидается в production.

Аналогия

Ты строишь мост и заявляешь, что он выдержит 10 тонн. Load Testing — это когда ты ставишь на мост ровно 10 тонн и смотришь: держит? Не гнётся? Не скрипит?

Если скрипит — нужна доработка. Если выдерживает спокойно — мост готов.


2. Цели Load Testing

  • Убедиться, что система выдерживает ожидаемое количество пользователей
  • Найти узкие места (bottlenecks) в производительности
  • Определить максимальную пропускную способность системы
  • Проверить время отклика под нагрузкой
  • Убедиться, что система восстанавливается после пиковой нагрузки

3. Ключевые метрики

МетрикаЧто измеряетХороший показатель
Response TimeВремя от запроса до ответа< 200 мс (API), < 2 сек (страница)
ThroughputКол-во запросов в секунду (RPS)Зависит от требований
Error Rate% ошибочных запросов< 1%
Concurrent UsersКол-во одновременных пользователейЗависит от требований
CPU / MemoryИспользование ресурсов сервераCPU < 70%, Memory < 80%
P95 / P9995-й и 99-й перцентиль времени откликаP95 < 500 мс

Что такое перцентили

P50 (медиана) = 150 мс: половина запросов быстрее 150 мс
P95 = 800 мс: 95% запросов быстрее 800 мс (только 5% медленнее)
P99 = 2000 мс: 99% запросов быстрее 2 сек (только 1% медленнее)

ВАЖНО: средний (average) показатель обманчив. Используй перцентили.
Пример: среднее 200 мс выглядит хорошо, но P99 может быть 5000 мс.

4. Типы нагрузочного тестирования

ТипОписаниеНагрузка
Load TestПроверка под ожидаемой нагрузкой100% от ожидаемой
Stress TestПроверка за пределами ожидаемой> 100% (до отказа)
Spike TestРезкий скачок нагрузки0 → 500% мгновенно
Soak TestДлительная нагрузка80% в течение 8–24 часов
Volume TestТестирование с большим объёмом данныхМиллионы записей в БД

5. Профиль нагрузки (Load Profile)

Перед тестированием нужно определить профиль нагрузки:

Сценарий: Интернет-магазин перед распродажей
────────────────────────────────────────────
Ожидаемые пользователи: 5 000 одновременно
Пиковая нагрузка: 10 000 одновременно (во время акции)

Критические операции:
- Главная страница: 2000 просмотров/мин
- Поиск товаров: 1500 запросов/мин
- Страница товара: 3000 просмотров/мин
- Добавление в корзину: 500 операций/мин
- Оформление заказа: 200 операций/мин

Требования к отклику:
- Главная страница: < 1.5 сек
- API поиска: < 500 мс
- Оформление заказа: < 3 сек

6. Пример Load Test с k6

k6 — современный инструмент для нагрузочного тестирования от Grafana Labs.

Установка

bashbash
# Windows (через Chocolatey)
choco install k6

# macOS
brew install k6

# Docker
docker run grafana/k6 run - <script.js

Базовый скрипт нагрузочного теста

javascriptjavascript
// load-test.js
import http from "k6/http";
import { sleep, check } from "k6";
import { Rate, Trend } from "k6/metrics";

const errorRate = new Rate("errors");
const searchDuration = new Trend("search_duration", true);

export const options = {
  stages: [
    { duration: "2m", target: 100 },   // плавный старт: 0 → 100 пользователей
    { duration: "5m", target: 500 },   // нарастание до ожидаемой нагрузки
    { duration: "10m", target: 500 },  // удержание пика
    { duration: "3m", target: 0 },     // плавный спад
  ],
  thresholds: {
    http_req_duration: ["p(95)<500"],  // 95% запросов быстрее 500 мс
    errors: ["rate<0.01"],             // ошибки < 1%
    http_req_failed: ["rate<0.01"],
  },
};

export default function () {
  // Тест: поиск товаров
  const searchStart = Date.now();
  const searchResponse = http.get("https://shop.example.com/api/products?q=laptop");
  const searchTime = Date.now() - searchStart;

  searchDuration.add(searchTime);
  errorRate.add(searchResponse.status !== 200);

  check(searchResponse, {
    "статус 200": (r) => r.status === 200,
    "ответ содержит products": (r) => r.json("products") !== null,
    "время отклика < 500мс": (r) => r.timings.duration < 500,
  });

  sleep(1); // пауза 1 секунда между итерациями
}

Запуск теста

bashbash
k6 run load-test.js

# С выводом в HTML-отчёт
k6 run --out html=report.html load-test.js

# С выводом в Grafana
k6 run --out influxdb=http://localhost:8086/k6 load-test.js

Пример вывода k6

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  (‾)  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: load-test.js
     output: -

  scenarios: (100.00%) 1 scenario, 500 max VUs, 20m30s max duration

running (20m00s), 0/500 VUs, 24500 complete and 0 interrupted iterations
default ✓ [======================================] 0/500 VUs  20m0s

     ✓ статус 200
     ✓ ответ содержит products
     ✗ время отклика < 500мс
       ↳ 91% — ✓ 22235 / ✗ 2265

     checks.........................: 97.05% ✓ 71265  ✗ 2265
     data_received..................: 1.2 GB 1.0 MB/s
     http_req_duration..............: avg=312ms min=45ms med=218ms max=4.2s p(90)=680ms p(95)=920ms
     http_req_failed................: 0.40%  ✓ 98     ✗ 24402

  FAILED! 1 thresholds breached.
  ✗ http_req_duration (p(95)<500)

Из отчёта видно: P95 = 920 мс при требовании 500 мс. Система не выдерживает нагрузку по требованиям.


7. Инструменты Load Testing

ИнструментЯзыкОсобенности
k6JavaScriptСовременный, скриптовый, CI-friendly
JMeterGUI + XMLКлассический, мощный, сложный
GatlingScala / JavaКод-first, хорошие отчёты
LocustPythonПростой, Pythonic
ArtilleryJavaScriptYAML-конфиги, облачный режим
wrkCLIПростые HTTP-бенчмарки

8. Где искать узкие места

После нагрузочного теста нужно анализировать:

Сервер → CPU / Memory / Disk I/O
База данных → медленные запросы (slow queries log)
Сеть → пропускная способность
Код → N+1 запросы, отсутствие кеширования
Архитектура → отсутствие горизонтального масштабирования

9. Частые ошибки

Тестируют на production
→ Нагрузочный тест — только на Staging или выделенном окружении.

Не используют реалистичный профиль нагрузки
→ 1000 запросов только к одной странице — не реалистично. Имитируй реальное поведение пользователей.

Смотрят только на среднее время ответа
→ Используй P95, P99. Средний показатель скрывает проблемы.

Не мониторят серверные ресурсы
→ Нагрузочный тест без мониторинга CPU/Memory бесполезен.


Итог

LOAD TESTING
├── Цель: убедиться, что система работает под ожидаемой нагрузкой
├── Метрики: Response Time, Throughput, Error Rate, P95, P99
├── Инструменты: k6, JMeter, Locust, Gatling
├── Профиль: нарастание → пик → удержание → спад
├── Не: не тестируй на production
└── После: анализируй bottlenecks и оптимизируй