Нагрузочне тестування API мобільного додатку
Мобільний додаток з 10 000 активних користувачів у пику — це не те саме, що 10 000 одночасних HTTP-запитів. Але близько. Екрани завантажуються паралельно, фонова синхронізація WorkManager та BGTaskScheduler працює незалежно від дій користувача, push-сповіщення може одночасно поднімати тисячі користувачів у додаток. Без нагрузочного тестування API бекенд ломається в найгірший момент.
Вибір інструменту
Три основні варіанти, кожен зі своєю нішею:
| Інструмент | Мова сценаріїв | Сильна сторона |
|---|---|---|
| k6 | JavaScript | Сучасний, CI-дружелюбний, низький поріг входу |
| JMeter | XML / GUI | Зрілий, багаті плагіни, візуальний тест-дизайн |
| Gatling | Scala / DSL | Точні метрики, зручні HTML-звіти |
Для більшості мобільних API вибирайте k6 — компактні сценарії, нативна інтеграція з Grafana, запуск у Docker без JVM.
Написання k6-сценарію для мобільного API
Типовий мобільний API має особливості: JWT-авторизація з коротким TTL, refresh-токени, gzip-стиск відповідей, іноді GraphQL замість REST. Сценарій повинен це враховувати.
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// Тестові аккаунты з CSV — не один аккаунт для всіх VU
const users = new SharedArray('users', () => open('./test_users.csv').split('\n').map(line => {
const [email, password] = line.split(',');
return { email, password };
}));
export const options = {
stages: [
{ duration: '2m', target: 100 }, // розгін
{ duration: '5m', target: 500 }, // плато
{ duration: '2m', target: 1000 }, // пік
{ duration: '1m', target: 0 }, // спад
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% запитів < 500ms
http_req_failed: ['rate<0.01'], // менше 1% помилок
},
};
export default function () {
const user = users[__VU % users.length];
// Логін + отримання токена
const loginRes = http.post('https://api.example.com/v1/auth/login', JSON.stringify({
email: user.email,
password: user.password,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, {
'login 200': (r) => r.status === 200,
'has token': (r) => r.json('data.access_token') !== undefined,
});
const token = loginRes.json('data.access_token');
sleep(1); // імітуємо поведінку користувача
// Завантаження ленти
const feedRes = http.get('https://api.example.com/v1/feed?page=1&limit=20', {
headers: { Authorization: `Bearer ${token}` },
});
check(feedRes, {
'feed 200': (r) => r.status === 200,
'feed has items': (r) => r.json('data.items').length > 0,
});
sleep(2);
}
SharedArray для користувачів — критично. Якщо всі Virtual Users використовують один аккаунт, бекенд може кешувати сесію й результати будуть некоректними.
Профілі нагрузки для мобільних додатків
Мобільний трафік нерівномірний. Три типові профілі:
Базовий — стабільна нагрузка 24/7. Перевіряємо, що при середній нагрузці p95 < 300 ms.
Пиковий — ранковий та вечірній всплеск. Ступінчастий ріст від 10% до 300% середнього за 5 хвилин.
Стрес-тест — намисно перевищуємо розраховане максимум, ищемо точку відмови. Поки error rate не перевищить 5% або latency не виросте в 10 раз.
Soak-тест — 70% від пікової протягом 4–8 годин. Ловить утечки пам'яті на бекенді, переповнення пула з'єднань до БД, ротацію логів.
Аналіз вузьких місць
k6 відправляє метрики у Grafana через InfluxDB або вбудований Prometheus remote write:
k6 run --out influxdb=http://localhost:8086/k6 scenario.js
Після прогону дивимось на:
-
http_req_durationпо перцентилям (p50, p90, p95, p99) -
http_req_blocked— час у черзі (високе значення = пул з'єднань виснажений) -
http_req_connecting— час встановлення TCP-з'єднання (високе = немає keep-alive) -
data_received— обсяг даних (неочікувано великий = немає gzip або зайві поля у відповіді)
Типові вузькі місця у мобільному API:
- N+1 запити до БД при завантаженні ленти з вложеними об'єктами
- Відсутність індекса на
user_id+created_atу таблиці постів - Синхронна відправка push-сповіщень у тілі запиту замість фонової черги
Запуск у CI
- name: Run k6 load test
uses: grafana/[email protected]
with:
filename: tests/load/api_test.js
flags: --duration 5m --vus 100
env:
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
На CI запускаємо полегшений профіль (100 VU, 5 хвилин) — для базової регресії по продуктивності. Повний стрес-тест — за розписанням або перед релізом.
Строки
3–5 днів — написання сценаріїв для основних едпойнтів, налаштування профілів нагрузки, перший прогон, аналіз результатів та звіт з рекомендаціями. Вартість розраховується індивідуально після вивчення API-документації.







