Ваш API падает под нагрузкой? Не знаете, сколько пользователей выдержит система? В этом руководстве мы разберем 3 лучших инструмента для нагрузочного тестирования API: Apache JMeter, Gatling и K6. Вы узнаете, как проводить load testing, анализировать метрики (RPS, latency, p99) и выявлять узкие места в вашем API.
📋 Содержание
🎯 Что такое Load Testing и зачем он нужен?
Load Testing (нагрузочное тестирование) — это процесс проверки производительности API под различной нагрузкой, чтобы определить, как система ведет себя при большом количестве одновременных запросов.
✅ Что вы узнаете из Load Testing:
- Максимальное количество RPS — сколько запросов в секунду выдерживает API
- Время отклика (latency) — как быстро API отвечает под нагрузкой
- Точка отказа — при какой нагрузке система начинает падать
- Узкие места — что именно тормозит (БД, CPU, сеть)
- Поведение под нагрузкой — как API масштабируется
Типы нагрузочного тестирования
- Load Testing — проверка под ожидаемой нагрузкой (например, 1000 RPS)
- Stress Testing — проверка предельных возможностей до отказа
- Spike Testing — проверка резких скачков нагрузки
- Soak Testing — длительное тестирование (часы/дни) для выявления утечек памяти
- Scalability Testing — проверка масштабируемости системы
📊 Ключевые метрики Load Testing
🚀 RPS (Requests Per Second)
Количество запросов в секунду — основная метрика throughput. Показывает, сколько запросов API обрабатывает за секунду.
Целевые значения:
- Небольшой API: 100-500 RPS
- Средний API: 1,000-5,000 RPS
- Highload API: 10,000+ RPS
⏱️ Latency (Response Time)
Время отклика — сколько времени занимает обработка одного запроса.
- p50 (медиана) — 50% запросов быстрее этого значения
- p95 — 95% запросов быстрее этого значения
- p99 — 99% запросов быстрее этого значения (самая важная метрика!)
Целевые значения p99:
- Отличное API: <100ms
- Хорошее API: 100-300ms
- Приемлемое API: 300-1000ms
- Медленное API: >1000ms
📈 Throughput
Пропускная способность — количество данных, передаваемых в единицу времени (KB/s, MB/s).
❌ Error Rate
Процент ошибок — доля запросов, завершившихся ошибкой (4xx, 5xx коды).
Допустимые значения:
- Отлично: <0.1%
- Хорошо: 0.1-1%
- Критично: >5%
🔧 Apache JMeter: универсальный инструмент
Apache JMeter — самый популярный open-source инструмент для нагрузочного тестирования, существует с 1998 года. Поддерживает HTTP, HTTPS, SOAP, REST API, WebSocket, JDBC, LDAP.
✅ Преимущества JMeter:
- GUI — графический интерфейс для создания тестов
- Огромное сообщество — миллионы пользователей, тысячи плагинов
- Универсальность — тестирование любых протоколов
- Recording — запись реальных HTTP-запросов из браузера
- Отчеты — HTML дашборды с графиками
❌ Недостатки JMeter:
- Высокое потребление памяти (Java JVM)
- GUI тормозит при больших тестах
- XML конфигурация сложна для версионирования
Установка JMeter
# macOS (Homebrew)
brew install jmeter
# Linux (Ubuntu)
sudo apt update
sudo apt install jmeter
# Windows
# Скачайте с https://jmeter.apache.org/download_jmeter.cgi
# Распакуйте и запустите bin/jmeter.bat
# Проверка установки
jmeter --version
Создание первого теста в JMeter
Способ 1: Через GUI
# Запустите GUI
jmeter
Пошагово:
- Создайте Thread Group (виртуальные пользователи)
- Добавьте HTTP Request Sampler
- Настройте URL и метод (GET/POST)
- Добавьте View Results Tree для просмотра результатов
- Добавьте Summary Report для метрик
- Запустите тест
Способ 2: JMX файл (для CI/CD)
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="API Load Test">
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
<collectionProp name="Arguments.arguments"/>
</elementProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Users">
<intProp name="ThreadGroup.num_threads">100</intProp>
<intProp name="ThreadGroup.ramp_time">10</intProp>
<longProp name="ThreadGroup.duration">60</longProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET /api/users">
<stringProp name="HTTPSampler.domain">api.example.com</stringProp>
<stringProp name="HTTPSampler.path">/api/users</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
Запуск JMeter в CLI (для CI/CD)
# Запуск теста без GUI
jmeter -n -t test-plan.jmx -l results.jtl -e -o report-folder
# Параметры:
# -n : non-GUI mode
# -t : путь к JMX файлу
# -l : файл результатов
# -e : генерировать HTML отчет
# -o : папка для отчета
# С параметрами (переменные)
jmeter -n -t test-plan.jmx \
-JUSERS=500 \
-JDURATION=300 \
-l results.jtl \
-e -o report
Пример теста API с POST запросом
// HTTP Request в JMeter
Method: POST
Path: /api/users
Headers:
Content-Type: application/json
Body:
{
"name": "Test User",
"email": "test@example.com"
}
⚡ Gatling: Scala DSL для highload
Gatling — современный инструмент для нагрузочного тестирования, написанный на Scala. Использует асинхронную архитектуру (Akka) для максимальной производительности.
✅ Преимущества Gatling:
- Высокая производительность — один сервер может генерировать 60K+ RPS
- Scala DSL — тесты как код, легко версионировать
- Красивые отчеты — HTML дашборды с интерактивными графиками
- Async архитектура — низкое потребление ресурсов
- Simulation API — продвинутые сценарии тестирования
❌ Недостатки Gatling:
- Требует знания Scala (барьер входа)
- Нет GUI (только код)
- Меньшее сообщество чем у JMeter
Установка Gatling
# Скачайте с https://gatling.io/open-source/
wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/3.10.3/gatling-charts-highcharts-bundle-3.10.3-bundle.zip
unzip gatling-charts-highcharts-bundle-3.10.3-bundle.zip
cd gatling-charts-highcharts-bundle-3.10.3
# Или через Maven/Gradle проект
Создание первого теста в Gatling
// src/test/scala/simulations/ApiLoadTest.scala
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class ApiLoadTest extends Simulation {
// HTTP Protocol Configuration
val httpProtocol = http
.baseUrl("https://api.example.com")
.acceptHeader("application/json")
.contentTypeHeader("application/json")
// Scenario 1: Get Users
val getUsersScenario = scenario("Get Users")
.exec(
http("GET /api/users")
.get("/api/users")
.check(status.is(200))
)
// Scenario 2: Create User
val createUserScenario = scenario("Create User")
.exec(
http("POST /api/users")
.post("/api/users")
.body(StringBody("""{"name":"Test","email":"test@example.com"}"""))
.check(status.is(201))
)
// Load Simulation
setUp(
getUsersScenario.inject(
rampUsers(100) during (10 seconds), // 100 пользователей за 10 секунд
constantUsersPerSec(50) during (60 seconds) // 50 RPS в течение 60 секунд
),
createUserScenario.inject(
rampUsers(50) during (10 seconds)
)
).protocols(httpProtocol)
.assertions(
global.responseTime.max.lt(1000), // max response time < 1000ms
global.successfulRequests.percent.gt(95) // 95%+ успешных запросов
)
}
Запуск Gatling теста
# Запуск через CLI
./bin/gatling.sh
# Выберите simulation из списка
# Или напрямую
./bin/gatling.sh -s simulations.ApiLoadTest
# В Maven проекте
mvn gatling:test -Dgatling.simulationClass=simulations.ApiLoadTest
Продвинутые паттерны Gatling
// Сложный сценарий с feeders и думающим временем
class AdvancedApiTest extends Simulation {
val httpProtocol = http.baseUrl("https://api.example.com")
// Feeder — CSV файл с тестовыми данными
val userFeeder = csv("users.csv").random
val scn = scenario("Complex User Journey")
.feed(userFeeder) // Подставляем данные из CSV
.exec(
http("Login")
.post("/api/auth/login")
.body(StringBody("""{"email":"${email}","password":"${password}"}"""))
.check(jsonPath("$.token").saveAs("authToken"))
)
.pause(1, 3) // Думающее время 1-3 секунды
.exec(
http("Get Profile")
.get("/api/users/me")
.header("Authorization", "Bearer ${authToken}")
)
.pause(2)
.exec(
http("Update Profile")
.put("/api/users/me")
.header("Authorization", "Bearer ${authToken}")
.body(StringBody("""{"name":"${name}"}"""))
)
.doIf(session => session("authToken").as[String].nonEmpty) {
exec(
http("Logout")
.post("/api/auth/logout")
.header("Authorization", "Bearer ${authToken}")
)
}
setUp(
scn.inject(
atOnceUsers(10), // 10 пользователей сразу
rampUsers(100) during (60 seconds),
constantUsersPerSec(20) during (5 minutes)
)
).protocols(httpProtocol)
}
🚀 K6: JavaScript для DevOps
K6 — современный open-source инструмент для нагрузочного тестирования от Grafana Labs. Написан на Go, использует JavaScript (ES6) для написания тестов.
✅ Преимущества K6:
- JavaScript — знакомый синтаксис для frontend/fullstack разработчиков
- DevOps-friendly — легкая интеграция в CI/CD
- Cloud-native — K6 Cloud для distributed load testing
- Производительность — написан на Go, низкое потребление ресурсов
- Extensions — поддержка WebSocket, gRPC, Kafka
❌ Недостатки K6:
- Нет GUI (только CLI)
- Меньше плагинов чем у JMeter
- Молодой проект (появился в 2017)
Установка K6
# macOS (Homebrew)
brew install k6
# Linux (Debian/Ubuntu)
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
# Windows (Chocolatey)
choco install k6
# Docker
docker pull grafana/k6
# Проверка установки
k6 version
Создание первого теста в K6
// api-load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
// Test configuration
export const options = {
stages: [
{ duration: '30s', target: 20 }, // Ramp up to 20 users
{ duration: '1m', target: 100 }, // Ramp up to 100 users
{ duration: '2m', target: 100 }, // Stay at 100 users
{ duration: '30s', target: 0 }, // Ramp down to 0 users
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95% < 500ms, 99% < 1s
http_req_failed: ['rate<0.01'], // Error rate < 1%
},
};
export default function () {
// GET request
const res = http.get('https://api.example.com/api/users');
// Assertions
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'has users': (r) => JSON.parse(r.body).length > 0,
});
// Think time
sleep(1);
}
Запуск K6 теста
# Запуск теста
k6 run api-load-test.js
# С параметрами
k6 run --vus 100 --duration 30s api-load-test.js
# С переменными окружения
k6 run -e API_URL=https://staging.api.com api-load-test.js
# С выводом метрик в InfluxDB
k6 run --out influxdb=http://localhost:8086/k6 api-load-test.js
# Docker
docker run --rm -v $(pwd):/scripts grafana/k6 run /scripts/api-load-test.js
Продвинутый K6 тест с несколькими сценариями
// advanced-api-test.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';
// Custom metrics
const loginFailureRate = new Rate('login_failures');
const apiResponseTime = new Trend('api_response_time');
const apiRequests = new Counter('api_requests');
export const options = {
scenarios: {
// Scenario 1: Constant load
constant_load: {
executor: 'constant-vus',
vus: 50,
duration: '5m',
},
// Scenario 2: Spike testing
spike_test: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '10s', target: 200 }, // Spike to 200 users
{ duration: '10s', target: 0 }, // Drop to 0
],
startTime: '5m', // Start after 5 minutes
},
},
thresholds: {
http_req_duration: ['p(99)<1000'],
login_failures: ['rate<0.05'],
api_requests: ['count>10000'],
},
};
const BASE_URL = __ENV.API_URL || 'https://api.example.com';
export function setup() {
// Setup code (runs once)
console.log(`Testing API: ${BASE_URL}`);
return { startTime: Date.now() };
}
export default function (data) {
// User journey
group('User Authentication', () => {
const loginRes = http.post(`${BASE_URL}/api/auth/login`, JSON.stringify({
email: 'test@example.com',
password: 'password123',
}), {
headers: { 'Content-Type': 'application/json' },
});
const success = check(loginRes, {
'login status is 200': (r) => r.status === 200,
'has auth token': (r) => JSON.parse(r.body).token !== undefined,
});
loginFailureRate.add(!success);
apiResponseTime.add(loginRes.timings.duration);
apiRequests.add(1);
if (success) {
const token = JSON.parse(loginRes.body).token;
group('Get User Profile', () => {
const profileRes = http.get(`${BASE_URL}/api/users/me`, {
headers: { 'Authorization': `Bearer ${token}` },
});
check(profileRes, {
'profile status is 200': (r) => r.status === 200,
});
apiResponseTime.add(profileRes.timings.duration);
apiRequests.add(1);
});
}
});
sleep(Math.random() * 3 + 1); // 1-4 seconds think time
}
export function teardown(data) {
// Teardown code (runs once)
const duration = (Date.now() - data.startTime) / 1000;
console.log(`Test completed in ${duration}s`);
}
📊 Сравнение JMeter vs Gatling vs K6
| Критерий | Apache JMeter | Gatling | K6 |
|---|---|---|---|
| Язык | Java | Scala | Go (тесты на JavaScript) |
| GUI | ✓ Есть | ✗ Нет | ✗ Нет |
| Синтаксис тестов | XML или GUI | Scala DSL | JavaScript (ES6) |
| Производительность | Средняя (JVM overhead) | Высокая (Akka async) | Очень высокая (Go native) |
| Потребление памяти | Высокое (~500MB+) | Среднее (~200MB) | Низкое (~50MB) |
| Max RPS (1 сервер) | ~10,000 RPS | ~60,000 RPS | ~30,000 RPS |
| Сообщество | Огромное | Среднее | Растущее |
| Плагины | Много | Средне | Средне |
| Отчеты | HTML + Plugins | Красивые HTML | CLI + JSON + InfluxDB |
| CI/CD интеграция | ✓ | ✓ | ✓✓ |
| Протоколы | HTTP, SOAP, JDBC, LDAP, FTP, etc. | HTTP, WebSocket, SSE | HTTP, WebSocket, gRPC |
| Кривая обучения | Низкая (GUI) | Высокая (Scala) | Средняя (JavaScript) |
| Лучше для | Enterprise, QA teams | Highload, Java/Scala teams | DevOps, Cloud-native, CI/CD |
| Open Source | ✓ Apache 2.0 | ✓ Apache 2.0 | ✓ AGPL |
| Cloud версия | BlazeMeter (платно) | Gatling Enterprise (платно) | K6 Cloud (freemium) |
🎯 Когда что использовать?
Используйте Apache JMeter если:
- Нужен GUI для создания тестов
- Команда QA без опыта программирования
- Enterprise проекты с разными протоколами (не только HTTP)
- Нужно записывать трафик из браузера (HTTP Proxy)
- Уже есть инфраструктура на JMeter
Используйте Gatling если:
- Нужна максимальная производительность (60K+ RPS)
- Команда знает Scala или JVM языки
- Highload проекты
- Нужны красивые отчеты из коробки
- Continuous load testing в CI/CD
Используйте K6 если:
- Команда знает JavaScript
- Cloud-native проекты (Kubernetes)
- DevOps культура, Infrastructure as Code
- Нужна легкая интеграция в CI/CD
- Хотите начать быстро (низкий порог входа)
✅ Best Practices для Load Testing
1. Тестируйте в production-like окружении
- Используйте staging с той же инфраструктурой что и production
- Те же версии БД, cache, load balancer
- Аналогичные объемы данных в БД
2. Начинайте с базового теста
- Smoke test: 1-5 виртуальных пользователей, проверка работоспособности
- Load test: Ожидаемая нагрузка (например, 80% от peak)
- Stress test: Постепенное увеличение до отказа
3. Используйте реалистичные сценарии
- Имитируйте поведение реальных пользователей
- Think time (пауза между запросами)
- Разные user flows (авторизация, просмотр, создание данных)
- Распределение запросов (80% GET, 15% POST, 5% DELETE)
4. Мониторьте не только API, но и инфраструктуру
- CPU, Memory, Disk I/O на серверах
- Database queries per second, connection pool
- Network latency, bandwidth
- Cache hit rate (Redis)
5. Запускайте тесты из нескольких регионов
- Distributed load testing (разные дата-центры)
- Проверка CDN и global load balancing
- Имитация реальной географии пользователей
6. Автоматизируйте Load Testing в CI/CD
- Smoke tests на каждый коммит
- Load tests перед релизом в production
- Performance regression tests (сравнение с baseline)
⚠️ Важные ограничения
- НЕ тестируйте production без разрешения (может быть расценено как DDoS атака)
- Предупредите команду перед большими тестами
- Используйте rate limiting на тестовом окружении
- Очищайте тестовые данные после тестов
🔍 Анализ узких мест
Как выявить bottleneck по метрикам
Высокий Latency + Низкий CPU
Проблема: Медленные запросы к БД или внешним API
Решение:
- Добавьте индексы в БД
- Оптимизируйте SQL запросы
- Кэшируйте результаты (Redis)
- Используйте connection pooling
Высокий CPU + Нормальный Latency
Проблема: CPU-intensive вычисления
Решение:
- Оптимизируйте алгоритмы
- Используйте async/non-blocking I/O
- Horizontal scaling (больше инстансов)
- Offload вычисления в background jobs
Высокий Memory + Out of Memory Errors
Проблема: Memory leaks или большие объекты в памяти
Решение:
- Profiling памяти (heap dumps)
- Исправьте memory leaks
- Используйте pagination для больших результатов
- Streaming для больших файлов
Растущий Error Rate
Проблема: Система не справляется с нагрузкой
Решение:
- Увеличьте timeout'ы
- Добавьте queue для async обработки
- Используйте Circuit Breaker pattern
- Horizontal scaling
Пример анализа результатов
# K6 результаты
✓ status is 200
✓ response time < 500ms
checks.........................: 98.50% ✓ 9850 ✗ 150
data_received..................: 45 MB 150 kB/s
data_sent......................: 12 MB 40 kB/s
http_req_blocked...............: avg=2.1ms min=0s med=1ms max=150ms p(90)=4ms p(95)=8ms
http_req_connecting............: avg=1.2ms min=0s med=0s max=80ms p(90)=2ms p(95)=5ms
http_req_duration..............: avg=245ms min=100ms med=220ms max=2.5s p(90)=400ms p(95)=600ms
{ expected_response:true }...: avg=240ms min=100ms med=215ms max=1.2s p(90)=390ms p(95)=580ms
http_req_failed................: 1.50% ✓ 150 ✗ 9850
http_req_receiving.............: avg=5ms min=1ms med=4ms max=50ms p(90)=10ms p(95)=15ms
http_req_sending...............: avg=2ms min=0s med=1ms max=30ms p(90)=4ms p(95)=8ms
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=238ms min=95ms med=210ms max=2.4s p(90)=385ms p(95)=590ms
http_reqs......................: 10000 333.33/s
iteration_duration.............: avg=1.24s min=1.1s med=1.22s max=3.5s p(90)=1.4s p(95)=1.6s
iterations.....................: 10000 333.33/s
vus............................: 100 min=100 max=100
vus_max........................: 100 min=100 max=100
# Анализ:
# ✅ p(95) = 600ms — хорошо (< 1s)
# ⚠️ p(max) = 2.5s — есть outliers, нужно исследовать
# ❌ Error rate 1.5% — выше нормы (должно быть < 0.1%)
# ✅ RPS 333/s — стабильный throughput
🔍 FAQ: Часто задаваемые вопросы
- Максимальное количество одновременных пользователей
- Время отклика при разных нагрузках
- Узкие места в системе (БД, CPU, сеть)
- Точку отказа (когда система начинает падать)
- Поведение при пиковых нагрузках
- QA команды без опыта программирования
- Нужна запись трафика из браузера
- Тестирование разных протоколов (не только HTTP)
- Нужна максимальная производительность (60K+ RPS)
- Команда знает Scala
- Красивые отчеты важны
- JavaScript знаком команде
- Cloud-native проекты
- Легкая интеграция в CI/CD
- RPS (Requests Per Second) — количество запросов в секунду
- Latency (p50, p95, p99) — время отклика (p99 самая важная!)
- Throughput — пропускная способность в байтах/сек
- Error Rate — процент ошибок (должен быть <0.1%)
- Concurrent Users — одновременные пользователи
Stress Testing проверяет предельные возможности — постепенно увеличивает нагрузку до отказа системы. Цель: найти точку разрушения (breaking point) и понять, как система деградирует под экстремальной нагрузкой.
- Перед каждым major релизом — убедиться что новые фичи не ухудшили производительность
- При изменении архитектуры — новая БД, cache, infrastructure
- Регулярно в CI/CD — smoke tests на каждый коммит, полные тесты раз в неделю
- После оптимизаций — проверить что улучшения действительно работают
- Перед Black Friday/пиками — убедиться что выдержите нагрузку
- Load Testing frontend без нагрузки на production backend
- Тестирование клиентского кода под нагрузкой (обработка ответов, error handling)
- Изоляции тестов — тестировать компоненты независимо
Протестируйте свой API сейчас
LightBox API — создайте Mock API для тестирования без нагрузки на production
Начать бесплатно →📝 Выводы
В этой статье мы рассмотрели 3 лучших инструмента для нагрузочного тестирования API:
- Apache JMeter — универсальный инструмент с GUI, лучший для enterprise и QA команд
- Gatling — Scala DSL, максимальная производительность для highload (60K+ RPS)
- K6 — JavaScript, DevOps-friendly, легкая интеграция в CI/CD
🎯 Главное:
- Load Testing критичен для production-ready API
- Тестируйте в production-like окружении
- Мониторьте p99 latency (не среднее время!)
- Автоматизируйте тесты в CI/CD
- Анализируйте узкие места и оптимизируйте
Related Articles
- API Performance: как оптимизировать производительность
- API Monitoring и Logging: как отслеживать здоровье API
- API Caching: стратегии кэширования для производительности