Нагрузочное тестирование с wrk и ab: быстрый старт
Нагрузочное тестирование сайта помогает ответить на практичные вопросы: сколько запросов выдерживает главная страница, где начинает расти задержка, при какой конкуренции появляются 502, 503 или таймауты, как ведёт себя API после релиза. Для быстрого старта не обязательно поднимать сложный стенд с распределёнными генераторами нагрузки — часто достаточно двух консольных утилит: ab и wrk.
ab удобен как самый простой инструмент: одна команда, понятный отчёт, минимум зависимостей. wrk полезен, когда нужно больше нагрузки, многопоточная работа, latency-распределение и Lua-сценарии. Оба инструмента не заменяют полноценное performance-тестирование, но хорошо подходят для первичной проверки гипотез и поиска очевидных узких мест.
Разберём, как безопасно подготовиться, установить инструменты, запустить первые тесты, прочитать результаты и не сделать неправильные выводы по красивой цифре Requests/sec.
Что именно проверяет нагрузочное тестирование сайта
Нагрузочный тест — это не просто «отправить много запросов». Хороший тест отвечает на конкретный вопрос. Например: «выдержит ли API 100 одновременных клиентов?», «насколько выросла задержка после включения нового middleware?», «не заканчиваются ли соединения к базе при пиковом трафике?».
Перед запуском стоит разделить несколько похожих, но разных задач.
1) Нагрузочное тестирование — проверяет работу системы под ожидаемой или чуть повышенной нагрузкой. Например, вы знаете, что обычно endpoint получает 50 RPS, и хотите проверить поведение при 100 RPS.
2) Стресс-тестирование — постепенно увеличивает нагрузку до отказа. Цель — найти точку, где начинают расти ошибки, latency или saturation: CPU, память, пул соединений, очередь запросов.
3) Smoke-тест производительности — короткая проверка после деплоя. Например, 30 секунд нагрузки на критичный endpoint, чтобы убедиться, что нет грубой деградации.
4) Benchmark — сравнение двух реализаций в одинаковых условиях. Например, было кэширование через БД, стало через Redis; было синхронное обращение к внешнему API, стало через очередь.
ab и wrk лучше всего подходят для smoke-тестов, простых benchmark и первых нагрузочных тестов сайта. Если нужны браузерные сценарии, логины, корзина, WebSocket, распределённая нагрузка из разных регионов и сложная модель пользователей, понадобятся другие инструменты. Но начинать всё равно полезно с простого HTTP-теста: он быстро показывает, есть ли проблема в самом backend, reverse proxy, TLS, базе или сети.
ab и wrk: в чём разница
ab — ApacheBench, старая утилита из экосистемы Apache HTTP Server. Она запускает фиксированное количество запросов с заданной конкуренцией и печатает отчёт. Главный плюс — простота. Главный минус — ограниченность: инструмент однопоточный, плохо подходит для генерации очень высокой нагрузки и даёт не самый богатый отчёт по latency.
wrk — более современный HTTP benchmarking tool. Он использует несколько потоков и неблокирующий I/O, поэтому с той же машины может генерировать больше нагрузки. У wrk есть режим --latency, который показывает распределение задержек, и поддержка Lua-скриптов для кастомных запросов.
Коротко:
| Задача | Лучше подойдёт |
|---|---|
| Быстро проверить endpoint одной командой | ab |
| Сравнить две версии страницы или API | ab или wrk |
| Нужна высокая нагрузка с одной машины | wrk |
| Нужно смотреть latency percentiles | wrk |
| Нужны POST-запросы с простой формой | ab |
| Нужен сценарий с динамическими заголовками или телом | wrk с Lua |
Есть и общий недостаток: оба инструмента тестируют HTTP-уровень, но не имитируют реального пользователя полностью. Браузер загружает HTML, CSS, JS, изображения, шрифты, выполняет JavaScript, использует кэш, переиспользует соединения. ab и wrk обычно бьют в один URL, поэтому результат стоит трактовать как проверку конкретного HTTP-ресурса, а не всего пользовательского опыта.
Если перед тестом вы проверяете endpoint вручную, удобно использовать curl; отдельный разбор есть в статье «Что такое curl и как им пользоваться».
Подготовка: как не сломать продакшен и не получить мусорные результаты
Перед первым запуском хочется сразу написать wrk -c1000, но это плохая идея. Нагрузочный тест сам по себе может стать причиной инцидента — особенно если тестируется продакшен, за reverse proxy стоит база данных, а лимиты не настроены.
1) Тестируйте только свои системы — не запускайте нагрузку по чужим сайтам и API. Даже «просто проверить» может выглядеть как атака. Если тестируете внешний сервис, нужно явное разрешение.
2) Начинайте с малой нагрузки — сначала -c 5, потом -c 20, потом -c 50. Резкий скачок до сотен соединений может упереться не в приложение, а в лимиты Nginx, firewall, пул БД или лимиты исходящих соединений на машине-генераторе.
3) Зафиксируйте окружение — версия приложения, конфиг Nginx, параметры базы, включён ли кэш, работает ли CDN, какой endpoint тестируется, откуда идёт нагрузка. Без этого результаты нельзя сравнивать.
4) Смотрите метрики сервера во время теста — CPU, RAM, load average, сеть, диск, количество соединений, ошибки Nginx, логи приложения. Одна цифра RPS почти ничего не объясняет. Для системного подхода пригодятся материалы про RED-метрики HTTP API и четыре золотых сигнала мониторинга.
5) Подготовьте откат — если тестируете продакшен, заранее договоритесь о времени, предупредите команду, проверьте доступ к логам и панели хостинга. Полезно включить внешний мониторинг доступности: например, Statuser может проверять сайт с заданным интервалом и прислать уведомление, если во время теста он перестанет отвечать.
6) Отделяйте лимит клиента от лимита сервера — если генератор нагрузки слабый, вы упрётесь в CPU, сеть или лимит файловых дескрипторов на клиентской машине. Тогда низкий RPS не означает, что сервер слабый.
Минимальный чеклист перед тестом: endpoint отвечает 200, логи пишутся, метрики доступны, понятны допустимые границы, есть команда остановки теста, тест не запускается в час пик.
Установка ab и wrk
ab обычно ставится вместе с пакетом утилит Apache.
Для Ubuntu/Debian:
sudo apt update
sudo apt install apache2-utils
ab -VДля CentOS/RHEL/Fedora:
sudo dnf install httpd-tools
ab -VДля macOS ab часто уже есть в системе. Если нет, проще поставить через Apache HTTP Server из Homebrew:
brew install httpd
ab -Vwrk в некоторых дистрибутивах доступен из репозитория, но часто его собирают из исходников.
Ubuntu/Debian:
sudo apt update
sudo apt install build-essential git libssl-dev zlib1g-dev
git clone https://github.com/wg/wrk.git
cd wrk
make
sudo cp wrk /usr/local/bin/
wrk -vmacOS:
brew install wrk
wrk -vЕсли вы запускаете тесты из Docker-контейнера или отдельной VM, убедитесь, что сеть между генератором и сервером не становится узким местом. Для контейнерных окружений полезно понимать базовые сетевые режимы Docker; см. разбор Docker networking.
Ещё один практический момент — лимит открытых файлов. При высокой конкуренции клиенту нужно много сокетов. Посмотреть текущий лимит можно так:
ulimit -nВременное увеличение в текущей shell-сессии:
ulimit -n 65535Это не всегда нужно для первых тестов, но при -c 1000 и выше лимиты быстро начинают влиять на результат.
Первый тест с ab
Самая простая команда:
ab -n 1000 -c 20 https://example.com/Здесь -n 1000 — всего 1000 запросов, -c 20 — 20 одновременных запросов. ab будет поддерживать до 20 параллельных запросов, пока не выполнит весь объём.
Для более похожего на обычный HTTP-клиент поведения часто включают keep-alive:
ab -k -n 5000 -c 50 https://example.com/Ключ -k включает HTTP KeepAlive, то есть переиспользование соединений. Без него каждый запрос может создавать новое TCP/TLS-соединение, и тест будет измерять не только приложение, но и стоимость постоянных handshake.
Полезные опции ab:
-n— общее число запросов;-c— concurrency, количество одновременных запросов;-k— keep-alive;-H— дополнительный HTTP-заголовок;-p— файл с телом POST-запроса;-T—Content-Typeдля POST/PUT;-s— timeout в секундах.
Пример с заголовком авторизации:
ab -k -n 2000 -c 30 \
-H "Authorization: Bearer TOKEN" \
https://api.example.com/v1/profileПример POST-запроса. Сначала создадим тело:
cat > payload.json <<'EOF'
{"email":"test@example.com","name":"Test User"}
EOFЗапустим тест:
ab -n 1000 -c 20 \
-p payload.json \
-T "application/json" \
https://api.example.com/v1/usersС POST-запросами нужно быть осторожнее: не отправляйте тестовые записи в боевую базу без очистки, не создавайте реальные платежи, письма, уведомления и задачи. Для таких тестов лучше использовать отдельное окружение, мок внешних сервисов или endpoint, который безопасно откатывает изменения.
В отчёте ab обратите внимание на поля:
Complete requests— сколько запросов завершилось;Failed requests— сколько запросов не удалось;Requests per second— средний RPS;Time per request— среднее время на запрос;Transfer rate— скорость передачи;Percentage of the requests served within a certain time— распределение времени ответа.
Если Failed requests не равен нулю, сначала разберите ошибки — высокий RPS с ошибками почти бесполезен. Для интерпретации HTTP-ответов пригодится справочник кодов HTTP.
Первый тест с wrk
Базовая команда wrk выглядит так:
wrk -t4 -c100 -d30s https://example.com/Здесь:
-t4— 4 потока генератора нагрузки;-c100— 100 открытых соединений;-d30s— длительность теста 30 секунд.
Для просмотра распределения задержек добавьте --latency:
wrk -t4 -c100 -d60s --latency https://example.com/Типичный вывод содержит:
Latency— средняя задержка и стандартное отклонение;Req/Sec— запросов в секунду на поток;Latency Distribution— percentiles, например50%,75%,90%,99%;Requests/sec— итоговый RPS;Transfer/sec— передача данных в секунду;Socket errors— ошибки соединений, чтения, записи, таймауты.
Для API с заголовком:
wrk -t4 -c80 -d45s --latency \
-H "Authorization: Bearer TOKEN" \
-H "Accept: application/json" \
https://api.example.com/v1/ordersДля POST в wrk удобнее использовать Lua-скрипт:
-- post.lua
wrk.method = "POST"
wrk.body = '{"email":"test@example.com","name":"Test User"}'
wrk.headers["Content-Type"] = "application/json"Запуск:
wrk -t4 -c50 -d30s --latency -s post.lua https://api.example.com/v1/usersLua-сценарии позволяют менять путь, тело, заголовки, генерировать разные данные. Но не стоит превращать wrk в полноценный сценарный тестировщик — его сила в высокой HTTP-нагрузке и компактных тестах.
Для первых проверок используйте длительность хотя бы 30s–60s. Слишком короткие тесты легко искажаются прогревом приложения, DNS, TLS, холодным кэшем, JIT-компиляцией, соединениями к базе. Если сервис работает на JVM, Node.js или Go, задержки могут меняться из-за GC; подробнее об этом — в статье «Garbage collection под нагрузкой».
Как читать результаты: RPS, latency, ошибки и saturation
Главная ошибка новичков — смотреть только на Requests/sec. Большой RPS приятен, но сам по себе не говорит, что пользователям хорошо. Если при этом p99 вырос до нескольких секунд, часть клиентов будет видеть тормоза или таймауты.
Смотрите минимум четыре группы показателей.
1) Throughput — сколько запросов в секунду обработано: Requests/sec в wrk или Requests per second в ab. Полезно для сравнения одинаковых тестов, но плохо объясняет пользовательский опыт без latency и ошибок.
2) Latency — время ответа. Среднее значение сглаживает проблемы, лучше смотреть percentiles: p50, p90, p95, p99. Если p50 нормальный, а p99 резко растёт, часть пользователей уже страдает. Отдельный разбор есть в статье «Что такое tail latency и почему p99 важнее среднего».
3) Errors — HTTP 5xx, 4xx, socket errors, timeouts. Ошибки 429 могут означать, что сработал rate limiting. 502 и 504 часто указывают на проблемы между reverse proxy и upstream. 503 может быть следствием перегрузки, обслуживания или лимитов.
4) Saturation — насколько исчерпаны ресурсы: CPU 100%, рост load average, заполненный пул соединений, очередь запросов, высокая disk I/O latency, переполненный listen backlog, лимит воркеров Nginx — всё это может быть реальной причиной деградации.
Хорошая интерпретация звучит не так: «сайт держит 800 RPS». Лучше: «endpoint /api/catalog при -c100 и keep-alive обрабатывает около 800 RPS, p95 держится в пределах приемлемого значения, ошибок нет, CPU приложения около верхней границы, база не упирается в I/O. При -c200 p99 резко растёт, появляются 504, в логах Nginx видны upstream timeout».
Если используете Nginx как reverse proxy, во время теста смотрите active connections, accepted/handled requests, ошибки upstream и access log. Подробнее — в статье «Мониторинг Nginx».
Типовые сценарии быстрых тестов
Ниже — несколько практичных шаблонов. Подставьте свой домен, endpoint и ожидаемые значения.
1) Проверить статическую страницу
wrk -t2 -c50 -d30s --latency https://example.com/Такой тест часто показывает возможности Nginx, CDN, TLS и сети, а не приложения. Если страница кэшируется, результат может быть очень высоким, но это не значит, что backend выдержит столько же динамических запросов.
2) Проверить API endpoint
wrk -t4 -c100 -d60s --latency \
-H "Accept: application/json" \
https://api.example.com/v1/catalogЕсли endpoint читает из базы, следите за временем SQL-запросов, пулом соединений, индексами и кэшем.
3) Сравнить до и после релиза
До релиза:
wrk -t4 -c100 -d60s --latency https://staging.example.com/api/v1/search?q=testПосле релиза — та же команда, тот же генератор, тот же endpoint, то же время. Менять сразу несколько условий нельзя: иначе непонятно, что повлияло на результат.
4) Найти точку деградации
Запускайте серию:
wrk -t4 -c20 -d30s --latency https://example.com/api/v1/catalog
wrk -t4 -c50 -d30s --latency https://example.com/api/v1/catalog
wrk -t4 -c100 -d30s --latency https://example.com/api/v1/catalog
wrk -t4 -c200 -d30s --latency https://example.com/api/v1/catalogПосле каждого теста фиксируйте RPS, p95/p99, ошибки, CPU, память, логи. Цель — увидеть не рекорд, а место, где система начинает вести себя хуже.
5) Проверить реакцию на лимиты
Если настроен rate limiting, например в Nginx, тест поможет убедиться, что сервер не падает, а корректно отвечает 429 Too Many Requests. Это лучше, чем неограниченно принимать запросы и затем получать 502 или 504. Про защитные лимиты можно почитать в материале «Как настроить Nginx rate limiting».
Частые ошибки при тестировании
Даже простые инструменты легко использовать неправильно. Вот ошибки, которые чаще всего портят результаты.
1) Тестировать с ноутбука по Wi-Fi — нестабильная сеть, энергосбережение, фоновые процессы и VPN искажают latency и RPS. Лучше запускать тест с отдельного сервера в той же или понятной сети.
2) Путать concurrency и пользователей — -c100 не означает «100 реальных пользователей». Это 100 одновременных соединений или запросов, а реальный пользователь делает паузы, загружает разные ресурсы и ходит по сценарию.
3) Игнорировать кэш — первый тест может прогреть кэш, второй покажет лучший результат. Это нормально, если вы тестируете кэшированный сценарий, но для честного сравнения нужно явно указать, тёплый кэш или холодный.
4) Тестировать только один URL — главная страница может быть быстрой, а поиск, корзина или отчёты — медленными. Выбирайте критичные endpoint: те, что дают нагрузку на базу, внешние API, авторизацию, очередь задач.
5) Не смотреть логи — нагрузочный тест без логов превращается в гадание. Если появились 5xx, нужно видеть, где именно: Nginx, приложение, база, внешний сервис.
6) Делать вывод по одному прогону — результаты могут плавать. Делайте несколько прогонов, убирайте явные выбросы, сравнивайте одинаковые условия.
7) Не учитывать защитные механизмы — WAF, CDN, rate limiting, bot protection и лимиты хостинга могут ограничить тест раньше, чем приложение. Иногда это хорошо: защита сработала. Но тогда не стоит называть результат пределом backend.
8) Гоняться за максимальным RPS — пользователям важнее стабильная задержка и отсутствие ошибок. Система, которая отвечает 300 RPS с ровной latency, часто полезнее системы, которая показывает 1000 RPS и периодически отдаёт таймауты.
Что делать после теста
После теста полезно оформить короткий отчёт. Достаточно зафиксировать условия, команды, результаты и выводы.
Минимальная структура:
- дата и время;
- версия приложения или commit;
- URL и метод;
- команда
abилиwrk; - откуда запускалась нагрузка;
- RPS;
- latency p50/p95/p99;
- количество ошибок;
- состояние CPU, памяти, сети, БД;
- вывод: что ограничивает систему и что проверить дальше.
Если под нагрузкой сайт переставал отвечать, не ограничивайтесь цифрами из wrk. Проверьте внешний симптом: был ли реальный downtime, сколько длился, какие коды получали пользователи. Здесь помогает мониторинг доступности: Statuser проверяет сайт с заданным интервалом и отправляет уведомление о сбое, а история проверок позволяет сопоставить момент теста с деградацией снаружи.
Дальше возможны разные действия:
1) Если упёрлись в приложение — профилируйте горячие участки, смотрите event loop lag, GC, блокирующие операции, синхронные вызовы внешних сервисов.
2) Если упёрлись в базу — проверьте медленные запросы, индексы, пул соединений, блокировки, I/O. Иногда один endpoint под нагрузкой создаёт очередь из-за отсутствующего индекса.
3) Если упёрлись в reverse proxy — проверьте лимиты worker connections, upstream timeouts, keep-alive, буферы, rate limiting, ошибки в access/error log.
4) Если упёрлись в сеть — проверьте bandwidth, retransmits, MTU, packet loss, балансировщик, CDN.
5) Если система падает каскадом — добавляйте backpressure, очереди, circuit breaker, лимиты и graceful degradation. Тема backpressure подробно разобрана в статье «Backpressure: как не положить сервис под нагрузкой».
Быстрый тест с ab или wrk — это стартовая точка. Он не отвечает на все вопросы, но быстро показывает, где копать: в приложении, базе, Nginx, сети или настройках лимитов.
FAQ
Можно ли запускать ab и wrk по продакшену?
Можно, если это ваш сервис, нагрузка согласована, есть мониторинг, логи и план остановки. Начинайте с малых значений -c и не тестируйте в пиковые часы без необходимости.
Что выбрать для первого теста: ab или wrk?
Для самой простой проверки подойдёт ab. Если нужны высокая нагрузка, несколько потоков и percentiles latency, лучше сразу взять wrk.
Почему wrk показывает высокий RPS, а пользователи всё равно жалуются?
Возможно, тестируется один быстрый endpoint, а реальная проблема в браузерной загрузке, базе, внешнем API, p99 latency или отдельных сценариях. Смотрите не только средний RPS, но и ошибки, tail latency и метрики сервера.
Сколько должен длиться нагрузочный тест?
Для быстрой проверки достаточно 30s–60s. Для более серьёзной проверки нужны более длинные прогоны, прогрев, повторяемость и наблюдение за ресурсами, кэшем, GC и базой данных.
Похожие статьи

Мониторинг Redis. Какие метрики реально важны под нагрузкой
Подробно объясняем, как мониторить Redis под высокой нагрузкой, какие метрики помогают находить деградации и почему latency важнее простого uptime.
27 мая 20267 мин

Garbage collection под нагрузкой: как GC влияет на latency (Java, Go, Node.js)
Объясняем влияние GC на latency, p99 и производительность приложений, рассматриваем GC в Java, Go, Node.js, даём практические советы по настройке и профилированию.
30 марта 20267 мин

Backpressure: как не положить сервис под нагрузкой
Разбираем механизм backpressure в распределённых системах: почему сервисы падают под нагрузкой, как управлять скоростью обработки запросов и какие паттерны помогают избежать перегрузки.
10 марта 20268 мин
Настроить мониторинг за 30 секунд
Надёжные уведомления о даунтаймах. Без ложных срабатываний