Приведи пример стресс-тестирования
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
# Пример проектирования и проведения стресс-теста
Контекст и цели тестирования
Предположим, у нас есть веб-сервис API для размещения онлайн-заказов (/api/v1/orders). В рамках стресс-тестирования (Stress Testing) нашей целью является не просто определение максимальной пропускной способности, а изучение поведения системы за пределами нормальных рабочих нагрузок, выявление "узких мест" (bottlenecks) и проверка механизмов восстановления после снятия нагрузки.
Ключевые цели для данного примера:
- Определить точку, в которой система перестает корректно обрабатывать запросы (например, увеличивается время ответа сверх SLA или растет процент ошибок).
- Найти "узкие места" в условиях экстремальной нагрузки: ЦПУ, память, сетевой ввод-вывод, блокировки в базе данных.
- Проверить механизмы деградации функциональности (graceful degradation) и устойчивости (resilience), если они заложены.
- Оценить время и процесс восстановления системы после пиковой нагрузки.
Сценарий стресс-теста
Мы сфокусируемся на критическом сценарии — создание нового заказа. Это операция, включающая несколько компонентов: веб-сервер, сервисный слой, базу данных и, возможно, внешний платежный шлюз.
Профиль нагрузки:
- Целевая нагрузка: 500 запросов в секунду (RPS) — это наш ожидаемый пиковый уровень.
- Стратегия нагрузки для стресс-теста: Мы будем плавно превышать этот лимит, чтобы создать стрессовую ситуацию.
* Этап 1 (разогрев): 10 минут на уровне 100 RPS.
* Этап 2 (рост): Увеличение нагрузки на 100 RPS каждые 5 минут.
* Этап 3 (стресс): После достижения 500 RPS продолжаем увеличивать нагрузку на 50 RPS каждые 5 минут до появления критических ошибок (>10%) или полного отказа.
* Этап 4 (восстановление): Резкое снижение нагрузки до 50 RPS и наблюдение за восстановлением системы в течение 15 минут.
Инструментарий
Для генерации нагрузки будем использовать k6 — современное средство нагрузочного тестирования. Его скрипты на JavaScript легко описывают сложные сценарии.
Пример скрипта k6 для нашего сценария:
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Trend, Rate } from 'k6/metrics';
// Кастомные метрики
let orderCreationDuration = new Trend('order_creation_duration');
let errorRate = new Rate('errors');
// Конфигурация теста
export let options = {
stages: [
{ duration: '10m', target: 100 }, // Разогрев
{ duration: '20m', target: 500 }, // Плавный рост до целевой нагрузки (100->500 за 4 шага)
{ duration: '20m', target: 700 }, // Превышение целевой нагрузки (500->700 за 4 шага)
{ duration: '15m', target: 50 }, // Фаза восстановления
],
thresholds: {
'http_req_duration{status:201}': ['p(95)<2000'], // 95% успешных запросов должны выполняться быстрее 2 сек
'errors': ['rate<0.05'], // Общий процент ошибок должен быть ниже 5%
},
};
// Генерация тестовых данных
function generateOrderPayload() {
const orderId = __VU * 1000000 + __ITER;
return JSON.stringify({
orderId: orderId,
productId: Math.floor(Math.random() * 100) + 1,
quantity: Math.floor(Math.random() * 5) + 1,
customerId: `customer_${Math.floor(Math.random() * 1000)}`
});
}
export default function () {
group('API Stress Test', function () {
// 1. Создание заказа (основная нагрузка)
let payload = generateOrderPayload();
let headers = { 'Content-Type': 'application/json' };
let res = http.post('http://api-under-test.local/api/v1/orders', payload, { headers: headers });
// Проверка результата и сбор метрик
let checkResult = check(res, {
'status is 201': (r) => r.status === 201,
'response time OK': (r) => r.timings.duration < 5000,
});
// Регистрация кастомных метрик
orderCreationDuration.add(res.timings.duration);
errorRate.add(!checkResult);
// 2. Небольшая пауза для имитации "мысли пользователя"
sleep(Math.random() * 0.1);
});
}
Мониторинг и анализ результатов
Во время выполнения теста мы мониторим не только метрики самого k6 (RPS, время ответа, процент ошибок), но и метрики инфраструктуры:
- Серверы приложения: Загрузка CPU и памяти, количество потоков/процессов, сетевой трафик, очередь на обработку (например, thread pool в Java).
- База данных: Активные соединения, время выполнения медленных запросов (slow queries), блокировки (locks), рост очереди на репликацию.
- Внешние зависимости: Статус вызовов к платежному шлюзу, время ответа.
Типичные находки при стресс-тесте и их интерпретация:
- Рост времени ответа и ошибок
5xxпосле 600 RPS: Это пороговая точка стресса. Если ошибки типа502 Bad Gatewayили503 Service Unavailable, вероятно, исчерпан лимит одновременных соединений на веб-сервере (например, в Nginxworker_connections). - Резкий рост использования CPU на уровне 650 RPS до 95-100%: Приложение упирается в вычислительные ресурсы. Необходим профилирование кода для поиска "горячих точек" или вертикальное масштабирование.
- Рост количества таймаутов соединения с базой данных: Может указывать на исчерпание пула соединений на стороне БД или приложения. Нужно проверить настройки
connection pool(HikariCP в Java, например). - Система не восстанавливается после снятия нагрузки: Критическая находка. Может быть вызвана утечкой памяти (memory leak), неубиваемыми "зомби-процессами" или блокировками в БД, требующими ручного вмешательства.
Заключение и рекомендации
По результатам стресс-теста формируется отчет с графиками, выводами и рекомендациями:
- Определение предельной нагрузки: Система поддерживает до 600 RPS с приемлемым временем ответа. При 650 RPS процент ошибок превышает 10%, что является точкой отказа.
- Выявление "узких мест": Основное ограничение — CPU сервера приложения. Вторичное — настройки пула соединений с БД.
- Рекомендации:
* Оптимизировать наиболее ресурсоемкие методы в коде (профилирование через **Java Flight Recorder** или **Async Profiler**).
* Увеличить лимиты параллельных соединений на веб-сервере и в пуле БД.
* Рассмотреть внедрение механизмов **rate limiting** и **circuit breaker** на входе в систему, чтобы более контролируемо отклонять избыточную нагрузку и защищать внутренние компоненты.
* Запланировать тест после внесения изменений для верификации улучшений.
Стресс-тестирование — это не разовое мероприятие, а циклический процесс, интегрированный в CI/CD конвейер для ключевых сценариев, который позволяет постоянно оценивать запас прочности системы при ее развитии.