Введение
Всем привет, меня зовут Сергей Прощаев. Я Tech Lead и руководитель направления Java | Kotlin разработки в FinTech, а также преподаю на курсах разработки и архитектуры в OTUS. В этой статье я расскажу не просто про кнопки в Apache JMeter, а про то, как перестать играть в нагрузочное тестирование и начать получать от него реальную пользу, которая спасает нервы в ночь релиза.
Видел много отчетов о нагрузочном тестировании. Знаете, что их объединяет в 80% случаев? Они рисуют красивые графики роста трафика и пишут «Система стабильна, ограничение — БД». На этом всё. А через неделю после релиза прод падает в час пик, потому что никто не проверил, как поведет себя кэш при 100% заполнении хипа или как работает пул соединений, когда один из инстансов сервиса начинает тупить.
В статье постараюсь разобрать это направление тестирования по шагам. Без воды, с Best Practice, которые используют команды, отвечающие за реальный Highload. А в конце — расскажу про открытый урок, где мы закрепим это всё на практике, чтобы вы не учились на своих дорогих ошибках.
Знакомство с Apache JMeter: Почему он, а не модный Gatling?
Часто можно услышать от новичков: «JMeter — это же прошлый век, UI некрасивый, Java‑стек, давай лучше Gatling, там кодом, это стильно, модно, молодежно». Доля правды в этом есть, но это взгляд разработчика, который хочет писать скрипты как код. Взгляд системного аналитика или инженера по нагрузке немного другой.
JMeter выигрывает не красотой, а инструментальной гибкостью и порогом входа для команды.
Протоколы. Помимо HTTP(S), он из коробки тянет JDBC, Kafka, JMS, gRPC (через плагины). Вам не нужно собирать отдельный проект на Scala, чтобы долбиться в базу данных прямыми запросами или проверить очередь сообщений.
Визуализация для «соседа». Когда вам нужно показать тестировщику, как именно мы парсим CSRF‑токен из страницы, открыть.jmx файл в UI куда проще, чем объяснять регулярки в коде.
Экосистема. Плагин JMeter Plugins Manager дает готовые графики процентилей и затупы потоков без необходимости писать экспортеры в Prometheus.
Конечно, у него есть недостатки — он прожорлив по памяти при отрисовке больших графиков в GUI. Но сильные инженеры знают правило: GUI — только для отладки. Нагрузку гоняем исключительно в CLI‑режиме. И дальше я покажу, как это работает.
Создание и отладка HTTPS‑скрипта: главная ловушка
Самая частая ловушка при создании скрипта — запись через HTTP(S) Test Script Recorder. Вы нажимаете кнопку Start, проходите сценарий в браузере, и... получаете гору запросов к google-analytics.com, fonts.gstatic.com и прочему мусору. А главное — получаете жестко зашитые ID сессий или токены, которые на втором запуске превращают тест в тыкву.
Мой подход к созданию чистого скрипта (Best Practice):
Transaction Controller. Каждый бизнес‑шаг (Логин, Поиск, Оплата) — отдельный контроллер. Это дает нам время отклика именно бизнес‑операции, а не кучки из 50 ресурсов.
HTTP Header Manager. Никогда не ленитесь его настраивать. Без корректного
Accept-Encoding: gzipвы будете грузить сеть и получать завышенное время ответа на статике.Регулярки и JSON Extractor. Вот где собака зарыта. Почти любое современное приложение шлет в ответе
authenticity_token,requestVerificationTokenилиSessionIdв куках.
Приведу простой пример обработки ответа в JMeter. Допустим, после логина сервер возвращает JSON с токеном, который нужно вставить в заголовок
X-Auth-Tokenдля всех следующих запросов.
// Это не код Java для выполнения, это пример настройки компонента JSR223 PostProcessor (Groovy) // Я предпочитаю Groovy, он шустрее BeanShell и меньше утилизирует CPU import groovy.json.JsonSlurper; def response = prev.getResponseDataAsString(); def json = new JsonSlurper().parseText(response); def token = json.data.authToken; // Кладем токен в переменные JMeter vars.put("AUTH_TOKEN", token); // Логируем, чтобы при отладке не гадать — вытащилось или нет log.info("Token extracted: " + token);
Почему это важно? В одном из проектов коллеги жаловались, что JMeter «глючит» — мол, проходит только один пользователь из десяти. Оказалось, что при разборе ответа через стандартный Boundary Extractor (по левой и правой границе) в ответе внезапно появлялся пробел в JSON из‑за особенностей форматирования бэкенда.
Регулярка "authToken":"(.*?)"ломалась, и 90% запросов шли без токена, получая 403. Деталь, которая стоила нескольких часов дебага. Поэтому — только JSON Extractor или Groovy.
Проведение нагрузочного теста: думаем как прод
Включать нагрузку с ноутбука через Wi‑Fi в кафе — это не совсем про тестирование (хотя почему и нет). Чтобы увидеть реальное поведение системы, нужно соблюсти несколько суровых правил.
Распределенный тест (Master‑Slave).Одна машина с Windows/Mac (мастер) управляет, несколько голых Linux‑серверов (слейвы) генерируют трафик. Почему Linux? Потому что там почти нет ограничений на количество ephemeral ports по умолчанию (они есть, но легче тюнятся через sysctl), по сравнению с Windows, и сетевая подсистема работает стабильнее.

На схеме показана классическая архитектура распределённого нагрузочного тестирования. С рабочего места инженера (ноутбук или десктоп) запускается Мастер JMeter — это управляющий узел, который содержит сценарий теста и раздаёт команды. Сам он трафик не генерирует. Реальную нагрузку создают Slave‑узлы — один или несколько выделенных серверов, обычно под Linux. Мастер синхронизирует их запуск и собирает агрегированные результаты.
Параллельно со стендом нагрузки работает мониторинг хоста (CPU, RAM, сеть), данные с которого также стекаются мастеру для последующей корреляции с временами отклика. Сами Slave‑узлы отправляют HTTPS‑запросы на целевой сервис, расположенный за балансировщиком. А тот, в свою очередь, взаимодействует с базой данных или кэшем. Такой подход позволяет не только проверить производительность приложения, но и увидеть, где именно находится узкое место: в самом приложении, на сетевом уровне или в инфраструктуре данных.
Профиль нагрузки — это не прямая линия вверх.
В реальной жизни нагрузка похожа на морские волны. Я всегда использую несколько плагинов:
Concurrency Thread Group (вместо стандартной) для удобного управления временем выхода на плато.
Throughput Shaping Timer — чтобы эмулировать не просто «100 потоков», а именно «500 запросов в секунду». Это критично для тестирования брокеров сообщений или API Gateway, которые могут начать шейпить трафик.
Анализ результатов: почему Average вводит в заблуждение?
Самое странное, что можно сделать — это открыть Summary Report и посмотреть в столбец Average (Среднее время).
Я видел отчеты, где писали: «Среднее время ответа 120 мс, всё хорошо». Потом выяснялось, что 99% запросов отрабатывали за 30 мс, а оставшийся 1% — за 12 000 мс (12 секунд!). Из‑за усреднения эти 12 секунд превратились в «приемлемые 120 мс». Пользователи при этом плевались и слали гневные тикеты.
На что можно полагаться:
Response Time Graph по процентилям (через плагин
jp@gc). Нас интересует p90, p95, p99. Если p90 = 30 мс, а p99 = 5 секунд — у нас проблема с долгим хвостом (GC pause или реконнект к БД).Transactions per Second (TPS). Ищем точку насыщения. Момент, когда добавляем виртуальных пользователей, а количество обработанных запросов в секунду перестает расти или падает — это и есть предел производительности текущего железа/конфигурации.
Latency vs Connect Time. Если Connect Time высокий, проблема на сетевом уровне или в рукопожатии TLS. Если Latency высокая при низком Connect Time — копаем приложение.
Генерация HTML‑отчётов: зачем платить за BlazeMeter?
Многие до сих пор делают скриншоты графиков из GUI или, что еще хуже, пытаются рисовать графики в Excel, экспортируя сырой .jtl. Хотя в JMeter уже несколько лет есть штатная фича генерации Dashboard Report.
После прогона теста в CLI у вас есть .jtl файл. Даже если он весит 10 ГБ (что бывает), одной командой вы получаете полноценный дашборд, который не стыдно отправить заказчику:
jmeter -g /path/to/result.jtl -o /path/to/report/folder
Открываете index.html и видите:
APDEX (индекс удовлетворенности пользователей).
Тепловую карту времени отклика.
Топ ошибок по кодам ответа.
Совет: Если отчет кажется вам скучным, поднимите флаг
jmeter.reportgenerator.overall_granularity=1000вuser.properties, чтобы сгладить пиковые выбросы на графиках и сделать картину чище.
Историческая справка: Как NASA «уронило» марсоход из‑за отсутствия нагрузочного теста
Марсоход Spirit попал в петлю перезагрузок из‑за проблемы с флэш‑памятью
В январе 2004 года, через 18 солов после посадки на Марс, марсоход NASA Spirit перестал нормально отвечать на команды и попал в бесконечную петлю перезагрузок.
Инженеры выяснили, что причина — в файловой системе флэш‑памяти (256 МБ). Из‑за большого количества накопившихся файлов (включая служебные логи, данные калибровки и другие записи, частично накопленные ещё во время перелёта) при попытке смонтировать файловую систему или работать с ней расходовалось слишком много оперативной памяти (RAM). Это приводило к ошибке, перезагрузке и повторению цикла.
Почему это произошло? На Земле тестирование проводилось в «тепличных» условиях и в основном проверяло функциональность. Никто не смоделировал реальную длительную эксплуатацию с накоплением сотен и тысяч файлов при ограниченных ресурсах. Когда ровер начал активно работать — создавать новые файлы во время сеансов связи, движения и научных измерений, — проявился недостаток robustness (устойчивости) файловой системы.
Корневая причина была не только в количестве файлов, но и в ошибках конфигурации двух модулей операционной системы, а также в особенностях используемой DOS‑подобной файловой системы, которая неэффективно обрабатывала большое количество записей.
NASA удалось спасти миссию: ровер перевели в «crippled mode» (работа без использования флэш‑памяти), удалили тысячи ненужных файлов, а позже полностью переформатировали флэш. После этого Spirit продолжил работу и в итоге проработал более 6 лет (до 2010 года).
Урок для тестирования
Эта история отлично иллюстрирует цену нагрузочного и стресс‑тестирования. Один короткий сеанс связи — всё работает. Тысячи операций с накоплением логов и файлов в условиях ограниченной RAM и флэш — система падает в петлю.
Инструменты вроде JMeter или специализированных тестов для embedded‑систем помогают заранее выявлять такие boundary‑кейсы: длительную нагрузку записи/чтения, рост количества файлов, утечки ресурсов и поведение при приближении к пределам памяти.
Заключение: нагрузочное тестирование — это не QA, это инженерия хаоса
Разбор работы с JMeter — это не заучивание горячих клавиш. Это тренировка системного мышления. Умение спроектировать профиль нагрузки, вытащить токен, понять процентиль и объяснить разработчикам, почему их «оптимизация» запроса привела к росту времени ответа в хвосте.
Плохой специалист нажимает зеленую кнопку Play и смотрит на среднее время. Хороший — строит распределенный кластер, имитирует смешанную нагрузку и находит узкое место за неделю до релиза, а не в 3 часа ночи после него.

Если этот разбор показался вам полезным и вы хотите разобраться в практиках команд, которые держат многомиллионный RPS, приглашаю вас на открытые уроки курса «Нагрузочное тестирование» в OTUS. На уроках мы научимся работать с JMeter, разберем типовые ошибки при анализе .jtl логов и заложим фундамент, чтобы вы могли уверенно отвечать на вопросы о производительности на собеседованиях.
➨ 28 апреля в 20:00 — «Первый нагрузочный тест в Apache JMeter». Записаться
➨ 19 мая в 20:00 — «Навыки нагрузочного тестирования и их роль в развитии инженера». Записаться
Уроки бесплатные, нужно только зарегистрироваться.