Архитектура20 февраля 2025 г. · 10 мин чтения

Нагрузочное тестирование микросервисов: паттерны и подводные камни

Тестирование распределённых систем требует думать о каскадных сбоях, circuit breaker'ах и реалистичном распределении трафика.

Автор: Команда Perfscale

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

Почему тестов на уровне сервиса недостаточно

Прогнать каждый сервис на 1 000 RPS в изоляции — почти ничего не узнать о том, как поведёт себя система, когда сервис A вызывает B, а тот вызывает C: каждый добавляет латентность, каждый способен на каскадный сбой.

Нужны сквозные (end-to-end) нагрузочные тесты, которые прогоняют систему через реальные пользовательские сценарии.

Паттерн 1: тестирование пользовательских сценариев

Моделируйте реалистичные пользовательские потоки вместо отдельных эндпоинтов:

import http from 'k6/http';
import { sleep, group } from 'k6';

export default function () {
  group('Browse catalog', () => {
    http.get(`${BASE_URL}/api/products`);
    sleep(1.5);
    http.get(`${BASE_URL}/api/products/42`);
    sleep(0.5);
  });

  group('Add to cart', () => {
    http.post(`${BASE_URL}/api/cart`, JSON.stringify({ productId: 42, qty: 1 }));
    sleep(1);
  });

  group('Checkout', () => {
    http.post(`${BASE_URL}/api/orders`);
    sleep(2);
  });
}

Так возникает реалистичное распределение трафика по сервисам — каталог получает в 3 раза больше нагрузки, чем сервис заказов.

Паттерн 2: хаос + нагрузка

Чистые нагрузочные тесты говорят о ёмкости в устойчивом состоянии. Комбинируйте их с chaos engineering, чтобы найти режимы отказа:

  • Убейте реплику посреди теста — трафик перенаправится чисто?
  • Впрысните 200 мс латентности в платёжный сервис — таймауты выше по цепочке каскадируют?
  • Заполните очередь — backpressure распространяется корректно?

Частые подводные камни

Thundering herd на разгоне: плавное наращивание VU спасает пулы соединений от исчерпания на старте.

Отсутствие think time: реальные пользователи не долбят API без пауз — добавляйте sleep(), чтобы имитировать реалистичный темп.

Конкуренция за общие тестовые данные: несколько VU, пишущих в одни и те же записи, создают lock contention, которого нет в продакшне. Используйте уникальные данные на каждого VU.

Игнорирование асинхронных потоков: если действие ставит работу в очередь, тест должен проверить, что очередь разгребается — а не только что enqueue вернул 202.