[Из песочницы] Вячеслав Смирнов — Ускоряем Apache JMeter

lzrpv9iyxgiwpbbd3ksu0-geabc.png

Apache JMeter не требует рекламы, но нечасто время уделяют скорости работы самих нагрузочных скриптов. Вячеслав рассматривает подходы к оптимальному написанию скриптов, что позволяет сэкономить на нагрузочных машинах и позволяет по-новому посмотреть на JMeter.

Apache JMeter является популярным инструментом тестирования производительности с большим количеством компонентов и возможностей. Одни и те же операции в Apache JMeter можно выполнить несколькими способами.

В проектах, где нужна высокая нагрузка, важным становится вопрос производительности нагрузочного скрипта. И хорошо бы иметь рейтинг производительности компонентов Apache JMeter и подходов к написанию скриптов.

Пользуясь средствами профилирования Java-приложений, такими как Java Flight Recorder, jVisualVM, SJK, имея доступ к исходным кодам инструмента, написав синтетические тесты и взяв примеры из практики, мы подготовили отчёт по тестированию производительности инструмента для тестирования производительности.

Доклад будет интересен инженерам по тестированию производительности, использующим Apache JMeter, как начинающим, так и опытным, а также разработчикам, использующим JVM/JDK в работе и занимающихся профилированием и оптимизацией кода.

Всем привет! Я сегодня расскажу, как ускорить Apache JMeter.

meuvl4an0gkyvtzlollq3qcpyp0.png

Немного о себе. Я бываю на конференциях в качестве докладчика, в качестве слушателя. Был преподавателем в университете, вел курсы по тестированию, писал статьи на Hubr. Это достоинства.

5iihoxcqn_mcvkfu-6jxfn_ic9o.png

Но есть у меня и недостатки. Их много. Мне говорят, что я достаточно неторопливый парень, но кое-что я умею делать быстро.

p8ga2eiipxhamm23nawcqbywmzo.png

Я ускоряю системы в банках и в других компаниях раньше это делал. И часто это делал с помощью Apache JMeter. Сегодня мы постараемся ускорять с его помощью не какие-то системы, а сам Apache JMeter.

oxvgy8wyjd5flhr4ed41va5gluk.png

Что такое JMeter, думаю, вы знаете.


  • Это один из известнейших и старейших продуктов на рынке. Он известен с 2003-го года.
  • Имеет плагины к более, чем к 50 системам, форматам, способам подготовки и обработки результатов.
  • Сохраняет статистику в ClickHouse, InfluxDB, Graphite. Многие системы сбора статистики, которые вы видели, отображают это в Grafana или в html-отчете.
  • Также его удобно встраивать в системы CI/CD.
  • Он имеет хорошую интеграцию с различными инструментами разработки.
  • И есть прекрасное сообщество.

jyi81jvnksfknshjhvisgtsesm8.png

Сегодня мы рассмотрим 5 основных тем. Это:


  1. HTTP-запросы. Как сделать отправку HTTP-запросов с максимальной интенсивностью.
  2. Как скачивать и отправлять файлы большого размера. И при этом мы столкнемся с какими-нибудь проблемами.
  3. Также PostProcessor на HTTP-ответы.
  4. PreProcessor.
  5. И расскажу об одном секретном оружие. Это секретное оружие позволит ускорить вообще все.

8pv7uxk3gvy0gpx3b1xzlr1pa8y.png

Моя цель развеять миф, возможно, известный многим, о том, что JMeter тормозит.

aandcuixd4-uapjszmds6icdmhg.png

А я бы хотел, чтобы вы добились следующих своих целей:


  • Укрепили свои знания о том, как разрабатывать тест таким образом, чтобы система тормозила, а не тесты.
  • Как делать так, чтобы запуски тестов были экономными, и вы могли запускать тесты во множестве экземпляров много раз и встраивать это в CI/CD-конвейер, где нужны практически непрерывные запуски нагрузки.
  • И разрабатывали тесты быстро и просто.

6x30vbslcnxrlusjzhhohgklo9a.png

Начнем с самого первого, с HTTP Request — максимальная интенсивность.

o1se_wirnjfoeix8xn0k0ngz_s4.png

Samplers.HTTP.Request.X. Простой тест. Несколько GET-запросов на локальный сервер. Для этой проверки я написал простенький скрипт. Назвал его Samplers.HTTP.Request.X. X означает, что он параметризирован. В нем параметризировано все. Сам тест достаточно простой. Давайте разберем из чего он состоит. Он отправляет GET-запросы в цикле и все.

yr4owfx5btbcyqravg24hatcqei.png

Запуск и остановка nginx в тесте, используя OS Process Sampler

В начале теста и в конце теста я запускаю локальный nginx-сервер. Использую для этого setup-катушку и tearDown-катушку, в которой с помощью OS Process Sampler запускается nginx и в конце останавливается.

hbwzcxnz92wsh9_lwdghwfdtyuc.png

Nginx с конфигурацией по умолчанию. Для скорости — два процесса вместо одного.

vclwdjdjas1uugrfwur4alzj6zo.png

А сердцем теста является одна Thread Group и внутри нее HTTP Request. Я в нем запараметризировал все: на какой сервер отправлять запрос, какую страницу скачивать. И настройки — по умолчанию такие, как Use KeepAlive. Она важна, на нее обратим внимание позже.

o65egwgcsatbnkwzif61qykx-fq.png

В данном тесте будем делать короткие сценарии, например, открыть стартовую страницу — это один запрос. И будем делать сценарии на 10 запросов — открыть стартовую страницу, ввести логин-пароль, или на 100, на 1000 запросов, используя здесь Loop Controller. В нем передавая параметр «RequestCount», я могу изменить длину сценария. Таким образом мы сможем проверить, насколько быстро JMeter при разной длине сценария будет работать.

n_15_nt0qptyhhczvgdoehyitye.png

А вверху у Thread Group параметры: сколько потоков — это Threads, сколько итераций — это LoopCount.

q11rvfq8wi1iitzu6o-opnb7clk.png

Таким образом сделаем серию экспериментов, в которых будем изменять параметры в трех направлениях:


  • Количество потоков: 1, 2, 3.
  • Длину сценария. Короткие сценарии на 1, 10, 50, 100 запросов.
  • И будем включать-отключать Keep-Alive.

Посмотрим, как изменения этих трех параметров скажутся на производительности.

Изменяя эти параметры, я провел достаточно много тестов. Давайте посмотрим на них.

yhfmqaytt0ru0b3tp7li-mlre2a.png

Если запустить тест только на одном потоке, то мы увидим следующую картину. На тесте с одним запросом (короткий запрос — просто открыть страницу) мы видим среднюю интенсивность, которую можно достичь. Это примерно 300–400 запросов в секунду.

Если мы увеличиваем длину сценария, допустим, до 10, то интенсивность возрастает примерно до 4 000 и т. д. по возрастающей.

Понятное дело, что с отключенным Keep-Alive такой опции нет, для нее каждое соединение новое.

xz3vrozd0o3ca8ut8rd53inqn00.png

При увеличении количества потоков (два потока, три потока) значения, конечно, растут.

qvakgqzxb_ks-4u4awoad0uh_ve.png

Между двумя и тремя потоками в моем конкретном тесте разница была небольшая.

На что стоит обратить внимание? Зачем я включал и отключал Keep-Alive? Давайте сравним, какой он дает эффект.

qyujxmvyyzr4zelwhiikgtm71ji.png

Что происходит на тестах, которые состоят только из одного запроса? Все benchmarks, которые я видел на JMeter, состояли как раз из одного запроса. Там ребята брали разные инструменты и добавляли туда ровно один запрос и сравнивали, как JMeter, который выполняет один запрос в цикле, соотносится с другими инструментами, которые делают то же самое.

JMeter, который выполняет только один запрос, не такой уж и быстрый. Даже если просто отключить Keep-Alive он станет быстрее. Мой тест это показывает.

mcsuae2xxnlerxlirfockmcdmlc.png

Если сделать тест подлиннее, то включение или отключение Keep-Alive уже практически не играет роли. Они примерно уже равны по скорости.

9qsahadu-e_r-tcf-fsutq5efmy.png

И вся сила Keep-Alive раскрывается, когда тест достаточно большой, т. е. в 50–100 запросов.

lqxfkudyvf5ejfmbyzwcyc8ycqq.png

Полезный вывод для тех, кто занимается нагрузкой или пишет benchmarks: Keep-Alive полезен. Он включен по умолчанию. Отключать его не надо. Но вся его сила раскрывается, когда сценарий достаточно длинный, хотя бы больше 10 запросов. Запомним это.

foviiank9myuys3nubvolsq6cvk.png

И запомним другой уже несколько негативный момент. Если вы пишите benchmarks на JMeter или на других инструментах, то не делайте их совсем простыми, т. е. из одного запроса. Они получаются слишком короткими. И там происходит избыток соединений, которые лишний раз не закрываются. Это нам снижает максимальную интенсивность.

vralochjerf2btyem6gwi45sz3c.png

Самые основные сценарии у нас все-таки состоят не из одного запроса и не из 100, а из чего-то среднего, например, из 10. Это зайти, залогиниться, куда-то перейти, выйти. Примерно это на 10 запросов.

Мы видим, что наш сценарий на 10 запросов выдавал примерно от 4 000 до 5 000 операций в секунду. Но мы видим также, что у JMeter есть достаточно большой потенциал.

ldc5wusa7p8e5mzaqhs3o6ms9w0.png

Попробуем разогнать наш простой сценарий с 4 000 до 16 000 операций в секунду. А, возможно, даже превысим эти значения. Думаю, у нас получится

-xlg5ym6as-rdrzark9vfjcprjy.png

Для этого нам понадобятся:


  • Инструменты мониторинга. Я использовал Telegraf, InfluxDB, Grafana.
  • Некоторые инструменты, которые позволяют замониторить сетевую подсистему. Для этого есть отличная утилита в Linux — это Netstat и другие консольные программы.
  • Настройки сетевой подсистемы ядра находятся в удобном доступе через файловую систему в Linux: /proc/sys/net/*.
  • Документация.
  • Профилировщики: SJK, Java Fligth Recorder.
  • И настройки самого JMeter.

Изменяя все это, попробуем ускорить с 4 000 до 16 000 наш тест.

aejxqj6zlpm-ezptuck5-pg5ija.png

Для начала запустим наш эталонный тест на 10 запросов без каких-либо изменений по новой. И замониторим не просто среднее значение интенсивности, а как она выглядела на графике. Я использовал JMeter Backend Listener. И это график из Grafana.

Я ожидал, что интенсивность будет стабильной, т. е. ровно 4 000–5 000. А она не стабильная, она скачет из нижней полки в 500 (на одном запросе) до верхней полочки в 16 000 (на 100 запросах). Видим всплески.

yncdqfgfs22uexjaqvn_ugpip_8.png

Кроме того, есть ошибки. Например, Non HTTP response code. В данном случае в Linux у меня выдается такая ошибка, как адрес недоступен. В Windows могут быть другие ошибки: невозможно установить соединение. Но у них одна и та же суть.

В данном случае я словил 5,24% ошибок. И тест уже не совсем успешный.

s9axvmijvhslyccelv_gpggacaw.png

Если запрофилировать JMeter в этот момент с помощью SJK, то профилирование показывает, что самый долгий метод, т. е. более 64% — это socketConnect. Т. е. JMeter не занимается тем, что отправляет запросы и получает ответы или что-то парсит, или что-то у него внутри медленное. Он занимается тем, что ожидает, что ему дадут новый сокет, чтобы подсоединиться к нашему локальному nginx. И на это тратится большая часть времени работы.

kff8atkw1f5gtrhznhajvau7yoq.png

Зная все это, попробуем как-то это ускорить. У нас есть три пути, как у витязя на распутье. Какие это пути?

amqbgenrytouhusf3vjj4hs9usw.png


  1. Мы можем поменять сам скрипт, т. е. что-то изменить в JMeter.
  2. Можем поменять настройки JMeter. Настроек достаточно много, есть что поменять.
  3. И можем поменять настройки операционной системы, т. е. как-то потюнить ядро, потюнить Linux, чтобы он работал быстрее.

xwsxbm60bmvqiw8hqw9bl6qlnj8.png

Скрипт JMeter: увеличить количество запросов (RequestCount) до 50. Изменим сам скрипт.

ebkjflieylxj0jxmrfz2ghpnnqa.png

Если вы помните, то я начал с того, что мы запускали тесты с разной длиной сценария: 1, 10, 50, 100. И было видно, что когда сценарий достаточно длинный (50 и 100), то нам выдавалось стабильно 16 000 или даже больше запросов в секунду.

Таким образом, если вы столкнулись с этими проблемами и хотите ускорить, то способ ускорения тестов с помощью правки сценария — это сделать в нем больше запросов.

В данном случае мы решили ограничь длинной в 10 запросов и не увеличивать, поэтому для нас увеличение — это не самый лучший подход. Попробуем какой-нибудь еще.

ud-bukmgnxauz22-hv2cc-5upn8.png

Может быть, HTTP Cache Manager ускорит работу? Сразу скажу, да, он ускоряет, но у меня тут маленькая оговорка: я загружаю достаточно простой html-документ. Он маленький.

w7_ek3xbiyuopcxxpq6ufyprsmc.png

И на маленьком документе, если добавить HTTP Cache Manager, то эффект будет небольшой. Он будет практически незаметным.

Я получил ту же самую интенсивность в 4 800. И получил примерно тот же самый процент ошибок, только чуть-чуть меньший.

xc4v0dfewbwbvyoeclrud2ff-w0.png

JMeter по-прежнему с HTTP Cache Manager«ом большую часть времени проводит на методе socketConnect. Т. е. мы ожидаем подключение к нашему локальному nginx.

ulhvbovsrseu3lzprre-lz5ox2g.png

Таким образом в данном конкретном случае модификацией скрипта разогнать тест не удалось, потому что мы решили не удлинять сценарий. У нас по постановке задачи он 10. А HTTP Cache Manager на маленьких ответах нам ничего не дал.

9jmw16wfr6y3fqunutqs2ysxoo4.png

Откатим все изменения назад. Представим, что мы вернулись в прошлое и никаких правок не было, HTTP Cache Manager не добавлялся.

scsd7lxsggbub2kl7aqo-nbp5zc.png

И попробуем какой-нибудь еще вариант. Покликаем настройки самого JMeter.

bgmyj6wree3w9v0s8np6pa0cb58.png

Первую настройку, которую я решил выбрать, которую, возможно, все видели, но никогда не меняли, это HttpClient4 на Java. По умолчанию используется HttpClient4.

Если сменить реализацию java-клиента (в данном случае я использовал 8-ую Java), то мы получим немалое ускорение в 3,3 раз и не получим ни одной ошибки вообще. Т. е. это практически настройка мечты. Можно ее, конечно, использовать с оговорками, потому что JavaClient может не все то, что поддерживается в HttpClient4. Он раскаченный специально для JMeter. Но если у вас простой HTTP, то почему бы и нет?

78sdnqn0-ymdt2xarr4qw9zwlxu.png

В этот момент профилирование показывает, что JMeter уже занялся делом. Большую часть времени он читает ответ. Какую-то часть времени он уже отправляет запросы. Т. е. мы не висим на методе socketConnect. Изменение этой настройки избавило нас от проблемы.

9jmw16wfr6y3fqunutqs2ysxoo4.png

Но откатимся снова в прошлое. Представим, что мы эту настройку не меняли. Поищем какой-нибудь еще способ.

qkxlgt7wie8mhnjesabzi4pdbqs.png

Есть еще одна настройка. Она позволяет отключить сброс состояния между двумя итерациями, т. е. мы выполнили 10 запросов, и там происходит сброс состояния. Мы закрываем все соединения, посылаем TCP — закройся, SSL — переинициализируйся и начинаем все по новой.

Можно сказать «false»: не сбрасывай состояние, установил и переиспользуй его.

mj6kiq659gfxqa7eqksockbowzu.png

Попробуем ее поменять. И, о чудо! Тест ускорился практически в 4 раза. В некоторых моментах я превысил даже полочку 20 000 в секунду. И это имея лишь слабенький ноутбук с 4-мя ядрами и три потока. Неплохо, но важно понимать, что эта настройка у нас сократила все-таки некоторое количество соединений, которые мы установили серверу, но она значительно ускорила интенсивность.

p06qcf_4wh6txz6cw5jix4mhnys.png

Резюмирую по этим двум настройкам. Если заменить реализацию HttpClient4, то мы ускорились на 3,3 раза. А если сделать так, чтобы использовался клиент по умолчанию, но не сбрасывал соединение, то ускорение практически в 4 раза. Неплохой результат.

9jmw16wfr6y3fqunutqs2ysxoo4.png

Но снова откатимся назад. Представим, что мы эти настройки не меняли. И попробуем что-то еще.

ljkgzv9hvshsjhldgqbwknr3zrg.png

А у нас еще есть настройки операционной системы. И там большое количество опций в сетевой подсистеме ядра, которые мы сейчас попробуем задействовать, чтобы наши соединения быстрее возвращались в JMeter, т. е. все было шустрее.

yrln1itiozapod9-7vdqfdyu6co.png

Для этого запускаем тест по новой, но включаем мониторинг всего, что есть. Допустим, я замониторил nginx. Видим, что подключения к nginx идут достаточно интенсивно, т. е. интенсивность подключения скачет от 0 до 1 200–1 300–1 400. И в пике получается, что максимальная планка подключений в секунду примерно в 10 раз меньше, чем от интенсивности в тесте. Это логично. Мы делаем 10 запросов — подключение, 10 запросов — подключение. Т. е. графики коррелирует.

vmh3gzf8dk9xeffrwmybhm0zkpq.png

График утилизации процессора, где красная линия — это системное время, а синяя линия — пользовательское время. А все остальные линии — это waits и что-то еще. И график показывает, что конфигурация по умолчанию большую часть времени тратится как раз на системные вызовы, а пользовательский код проигрывает в 3,5 раза.

-oyacgokxgbh-cqegie_uuwwjkm.png

Значит, обратим внимание на различные системные метрики. Прежде всего на сетевые. И мониторинг метрик, который в Grafana, Telegraf называется … Netstat, в частности, метрика tcp_time_wati, показывает, что с момента начала теста мы достаточно быстро доходим до полочки, которая отчетливо вырисовывается в 28 231. Вот такое магическое число. И держимся на этой полочке весь тест. Иногда лишь прогибаясь, но потом возвращаясь. Т. е. что-то нас держит, ограничивает значение tcp_time_wait этим числом.

rbjfqbvmdczzwkbmx4cfuwcrcgy.png

Чтобы замониторить, кто подсоединяется к нашему локальному nginx (я локальный nginx запускал на локальном порту 5555), используем утилиту NetStat.

Она показывает, что там лишь одно соединение от JMeter к nginx установлено, а все остальные находятся в состоянии TIME_WAIT.Т. е. все эти 28 000 — это JMeter установил соединение, потом итерация закончилась, и он сказал: «Соединение закройся». И теперь соединение ожидает, когда nginx ему подтвердит: «Да, я соглашаюсь, давай закроемся», т. е. происходит некое согласование. Оно происходит не мгновенно, поэтому какое-то время соединение в этом состоянии висит.

iusdatax8gpsmq6x6a8pveufoeq.png

Поиск по документации быстро нас выводит на опцию, которая отвечает за количество соединений, которые мы можем открыть. Она называется local_port_range. Ее значение по умолчанию в Ubuntu от 32 000 практически до 61 000.

И если из 61 000 вычислить 32 000, то мы получим наши 28 000.

Посмотреть эту настройку можно с помощью утилиты cat, выполнив команду «cat /название метрики», и нам выведется его значение по умолчанию.

6roriysrdtlgsoxe8eyvvavawim.png

А чтобы изменить эту метрику можно воспользоваться командой «echo», вызвав ее с админскими правами.

Допустим, отодвинем левую границу в 32 000 еще левее и сделаем наш диапазон несколько больше, т. е. в два раза: от 1 025 до 60 999. Совсем до нулей отодвигать не стал, потому что не знаю, к каким последствиям это привело бы.

Очень мне помог @blog.kireev.pro. Там очень хорошо описано, что такое tcp_time_wait и почему это возникает, и что с этим делать.

uefrwczdlfwcgxubcqmqgzpjs7m.png

Мы меняем настройку. Перезапускаем тест. И у нас есть ускорение примерно в 1,6 раза. Теперь интенсивность скачет не от 500 до 16 000, а от 3 500 до 19 000. Это уже хорошо. Средняя составила 7 700.

2_sbq4ezafxbwirhjtxpigalgga.png

Ошибки ушли, что немало важно. Практически победа.

mkb_emmf3_6bew019voo1phm3kk.png

Если посмотреть профилировщиком что же сейчас выполняет JMeter, он по-прежнему висит на методе socketConnect.

hbnl6obiuat7zb3jlzeuj1xroz8.png

Мы ускорились, избавились от ошибок Address not available за счет опции ip_local_port_range.

xnrcwd-drjs0f_qfrz1rzilnwa0.png

Но мы лишь полечили симптомы, проблема осталась, мы все еще висим на socketConnect. И если обратно вернуться на метрику Netstat: tcp_time_wait, т. е. замониторить ее с помощью Telegraf, посмотреть Grafana, то мы видим, что мы больше не упираемся в 28 000. Мы теперь упираемся в 32 768. Снова замечательное число, думаю, знакомое айтишникам. И что-то нам подсказывает, что это очередной лимит.

o8jjq9cyjjsamu9xwylb4kvsqws.png

И, наверное, мы можем его найти. Такой лимит есть. И есть такая опция tw_ buckets максимальное значение, максимальная длина очереди TIME_WAIT.

Если снова выполнить команду «cat», она подскажет, что в Ubuntu по умолчанию значение как раз равно 32 768.

lfmibk0bqrofhjvx-3btjqctmuc.png

И мы его можем изменить, допустим, в два раза, записав туда значение в 65 000. Давайте это сделаем.

02y5rcezil2cwqnziim5lpa6ql8.png

Перезапустим тест, но увы, нагрузка осталась той же. Среднее значение примерно то же. Она по-прежнему скачет от 3 500 до 19 000.

4a2aplhe7l-ft3sprlaveedn3je.png

Какие плюсы? Tcp_time_wait упирался в полочку в 32 768, а теперь он не упирается. Он вначале заскакивает на 46 000. И по ходу теста плавает в более свободном интервале, т. е. полочки нет, но разогнаться нам это не помогло.

k7fly8lqbsj8dwqcognkrx8uf8e.png

Запомним этот момент. Это опыт. Если увеличить local_port_range, а потом сверху, не откатываясь назад на машине времени еще увеличить tw_buckets, то tw_buckets как-то не добавляет нам ничего хорошего, но мы уходим от полочки. И это плюс.

rqu044pxw2q7tkv8huneijniq0s.png

Профилирование показывает, что с двумя настройками JMeter по-прежнему висит на методе socketConnect, но уже меньшую долю — около 50%. Будем искать настройки дальше.

sptnkuge4dqlfq5pstmsvavd-rc.png

И поиск по настройкам нас приводит к тому, что можно переиспользовать TIME_WAIT-соединение. Для этого нужно записать »1» в настройку tcp_tw_reuse. По умолчанию там »0».

qjggdsmsjx3t2qc_0m9psqanrt0.png

Если это сделать, то интенсивность на данной машине без каких-либо правок теста, без каких-либо опций увеличивается в 3,6 раза и достигает 19 000 иногда. Становится более стабильной. Конечно, мы избавляемся от ошибок.

mx5fqonabamy53k0kegtau4hzfg.png

Профилирование показывает, что JMeter уже занялся делом. Он делает socketRead, иногда socketWrite, т. е. делает осмысленные действия. Теперь мы читаем заголовок ответа и тратим на это большую часть времени.

h88uwdj-aqqtpakuupwcj6s1zmq.png

Процессор «задышал», мы не тратим большую часть времени в системных вызовах, выполняется наш пользовательский код. Думаю, JMeter заработал.

o8kq4rvpt-1jlelqtrwi7v4hypq.png

Но снова упираемся в подозрительную полочку по TIME_WAIT. Но на данный момент мы уже выполнили все наши задачи. Если изначально задача была — побить планку в 16 000, то мы ее побили. Какая такая есть настройка, которая нас ограничивает в 30 000, я не знаю, но есть хорошее руководство у других инструментов по тестированию производительности. Например, Gatling.

k4qc80zeqyvoe571d1bficz9mru.png

https://bit.ly/gatling-tuning

https://gatling.io/docs/current/general/operations/

Это можно найти в документации Gatling, в разделе General/ Operations. Там есть настройки по Java и тюнинговым операционным системам.

va4tef4gfi0o3k7kgp6stbpvk1w.png

Некоторые из них пересекаются с теми, которые я уже успел использовать. Например, local_port_range.

Но эти настройки нужно использовать по одной и с осторожностью. Почему? Например, некоторые настройки, рекомендации говорят, что в 5, 6 строчках мы увеличим максимальное количество файлов, которые доступны на данной системе. Но по секрету скажу, что в операционной системе Linux настройка nr_open по умолчанию равно миллиону. И если бездумно выполнить рекомендацию, то мы ее не увеличим, а понизим до 300 000.

Вторая настройка file-max ровна 70 000 с лишним, и мы ее понизим. Поэтому нужно внимательно смотреть на рекомендации, по одной применять и тем или иным способом вы ускорите работу.

sqidmwgjcd7em74hfztzdvw3hme.png

Там есть хорошая настройка tw_reuse = 1.

e49ulnx1vleb1rhmsivduzk3tp0.png

https://yandextank.readthedocs.io/en/develop/generator_tuning.html#tuning

И есть хорошая рекомендация от Yandex.Tank. Они тоже пересекаются, их тоже нужно смотреть по одной, внимательно читая.

Например, там есть некоторые старые настройки tw_recycle. Tw_reuse рекомендуется выставить в ноль. Думаю, у разработчиков Gatling были свои причины, чтобы сделать так. Возможно, эти две настройки на другой версии ядра давали тот же самый эффект.

Смотрите настройки Yandex.Tank, Gatling, вы можете еще что-то дополнительно затюнить, мы уже в эти подробности вдаваться не будем.

75bwgvizme9s5n-tycazs_bbuee.png

Пока остановимся на том, что tw_reuse позволил нам в 3,6 раза ускорить тест. И это здорово.

odkvcmconkqirhqzabrdqeanqmo.png

Если посмотреть на все настройки целиком, то что получилось?


  • Если не сбрасывать состояние, а выставить значение reset_state в false, то мы ускорились 3,9 раза.
  • Если сменить Httpsampler на стандартную реализацию Java, то в 3,3 раза.
  • А по настройкам Linux c tw_reuse в 3,6 раза при наличии всех других настроек.
  • И local_port_range в два раза нам все-таки ускорило тест.

Прошу обратить внимание на опции из других инструментов. Они нам пригодятся.

jukolmbtdknqcddskzqp3k1gchg.png

Вы можете увидеть, что состояние нормальное у JMeter — это не когда он висит в методе в socketConnect. А когда он примерно треть висит в read. 20% он выставляет timeout — это некие сервисные функции. 11% в BackendListener. И 12% отправляет запросы. Вот это состояние нормального и здорового JMeter.

qqebuzmry6p5gm1_8pnyqn5uwoy.png

Попробовать все эти настройки вы можете самостоятельно, можете как-то их смешать. Думаю, у вас все получится.

a21bbwcyj1345qcelj7jcfmg-6g.png

А мы пока перейдем к следующей части. Это как скачивать файлы большого размера в JMeter, во что мы упремся при этом и как мы это ускорим.

csgthagbxys6gstun_dptxe9ig8.png

Последовательное скачивание файла, используя Thread Group на один поток и 200 итераций, имея 4 Гбайт Heap Size

Для этого теста я придумал такую задачу, что мы будем скачивать 200 гигабайт последовательно. Создается один поток и в нем 200 раз скачивается гигабайт. И посмотрим, как JMeter справляется, и с какими ограничениями мы столкнемся.

Файл точного размера 1 гигабайт. Я его создал с помощью утилиты DD. Думаю, все ей доверяют.

ro5dapl9yncjdhlrv418xj8lde8.png

Запускаем тест с настройками по умолчанию. И получаем неплохой результат. 5 минут. Скорость загрузки каждого гигабайта достаточно стабильна. В целом 1,5 секунды. Но мы употребили всю память, которую я выделял по JMeter. Я выделял 4 гигабайта.

gc7digzkv13wjahtj_dhzaigo28.png

Если посмотреть с помощью Java Fligth Recorder, как утилизируется память, то вот этот синий график показывает, что память постоянно выделяется и очищается, т. е. сборок мусора за время скачивания этих 200 гигабайт было неимоверное количество. А средний memory traffic (метрика отображается в правом нижнем углу данной вкладки) показывает, что мы примерно выделяли память со скоростью 2 гигабайта в секунду. Т. е. JMeter скачивал 200 гигабайт и каждую секунду выделял 2 гигабайта, а JFR их очищал. Думаю, был большой overhead только на JFR.

gx1mslsx6u0rhjzbmzg8urxfpxq.png

Профилирование с помощью SJK показывало, что мы висели на socketRead, т. е. читали, выделяли память, очищали.

1q_tk-_vuryktduj_lmjv5atlco.png

Важно заметить следующую штуку. Если хоть чуть-чуть увеличить размер файла, имея Heap в 4 ГБайта, допустим, я скачивал 1 гигабайт, а буду скачивать 1,1 гигабайт, то сразу получаю OutOfMemoryError. Это соотношение важно запомнить. Я думаю, что если скачивать 2 гигабайта, то мне нужно HEAP 8 гигабайт, т. е. большой объем памяти нужно на нагрузочной станции, чтобы скачивать большие документы с настройками по умолчанию.

tv_p4zq1xvyps83rilb7a-vecmo.png

Запомним этот опыт, как несколько негативный. Httpsampler с настройками по умолчанию работает быстро, но требует гигантское количество оперативной памяти.

kff8atkw1f5gtrhznhajvau7yoq.png

И на этой неутешительной ноте, я думаю, что у нас есть три пути, чтобы как-то это исправить.

amqbgenrytouhusf3vjj4hs9usw.png

Изменить скрипт, изменить настройки операционной системы или изменить настройки JMeter.

xwsxbm60bmvqiw8hqw9bl6qlnj8.png

Начнем с того, что изменим скрипт.

qnc0bklrobjbtmi2cevkvtzuu8e.png

Документация по HTTP Request подсказывает нам, что есть такая опция Save response as MD5 hash. Что она позволяет? Она позволяет не сохранять все тело ответа, а превратить его в MD5 hash. И мы, я думаю, сэкономим на памяти. Т. е. мы уже не будем хранить столько всего.

mf6zzeht3bkvtojdt9_mxfmpayc.png

Поставим ее, запустим тест. И, правда, мы память сэкономили. Утилизация памяти стала равна нулю. Но мы несколько удлинили сам процесс скачивания, т. е. аж до 18 минут. Среднее время теперь составило 5,5 секунд на гигабайт.

bf-ygyeqf-mvynfflehbqpxh6w0.png

Если запустить Java Flight Recorder, то он покажет, что мы вообще не видим синюю линию. Т. е. память с начала теста чуть-чуть выделилась и тут же ее сборщик мусора очистил ее все. Памяти действительно ноль, ее нет.

i3ouc7gdbmc_5ppxr0mejbqvkwk.png

Профилирование показывает, что MD5 занял 66%, т. е. в два раза больше, чем оставшуюся часть. 33% — socketRead. Таким образом, если поставить галочку MD5, то мы замедляем наше скачивание ровно в три раза. К нашему одному socketRead добавляется две части на MD5.

9mfyzud220dkzp7ajnn5yx0ttiq.png

Запомним все-таки MD5 как компонент, который не врет. Там написано, что он сокращает утилизацию памяти. Он ее сокращает до нуля, но он замедляет скачивание до трех раз.

9jmw16wfr6y3fqunutqs2ysxoo4.png

Откатим эту настройку назад, представим, что у нас снова эталонный скрипт.

iwgx0r3ifheff9xxspfmuxwmecc.png

Мы скачиваем наши 200 гигабайт, но зададим себе следующую задачу — как скачать и быстро, и потратив немного памяти? Есть ли такой способ в JMeter? Думаю, многие скачивали гигабайты с помощью консольных утилит: wget, curl. И с помощью OS Process Sampler консольные утилиты и скрипты можно легко встраивать в JMeter. Давайте так и поступим.

k2m-mxpnsw_u0v3ll46erhwvscs.png

Для этого я написал несложный скрипт. Это для Linux, который использует wget. Назвал его wget-download.sh. В него передаются параметр «url». И он скачивает, отправляя весь результат в dev/null, чтобы у меня на нагрузочной станции диск не переполнился. Если проводить 2–3 итерации теста, то 400–600 гигабайт и все, станция потеряна.

uhhsvmhn7i-xnkw746wzksza30y.png

Тот же самый скрипт можно написать и для Windows. Wget… с помощью MinGW. Dev/null только в Windows выглядит иначе.

2n0wwbcxyqtlvuligdlhbxwzlbs.png

Кроме того, Wget может ограничить и скорость скачивания. Мы можем притвориться, допустим, мобильным клиентом.

xtvnwjx2jmm60izwg_puvzz6hy4.png

Это аналог опции characters per second, которая есть в JMeter, но она там задает ограничение на весь JMeter в целом, а Wget мы можем сказать, чтобы конкретный запрос скачивался медленно, а другой запрос быстро, т. е. это несколько удобнее.

xtvltjpetbtmu86xoyio1lbbyue.png

Кроме того, мы можем добавлять заголовки. Можем сказать, что мы поддерживаем gzip.

oxmf0wpav-fiudyjjvgbjsaro20.png

И даже этот gzip можем разжимать с помощью gzip. Т. е. bash утилиты дают нам полную свободу. Мы можем действовать как отличный консольный клиент.

r-7m9wwuaexoyjd37h_iq99ejto.png

Но чтобы это выглядело в JMeter как родное, нужно воспользоваться некоторой хитростью. Я ей воспользовался. Она называется SampleResult. Есть программный доступ из PostProcessor, который позволяет BodySize установить, content-type, все загол

© Habrahabr.ru