Інструменти тестування продуктивності. Частина 3
Всім привіт. Я Сергій Моренець, розробник, викладач, спікер і технічний письменник, хочу поділитися з вами своїм досвідом роботи з такою цікавою темою, як тестування ефективності або продуктивності програми.
У першій частині цієї статті я зробив огляд найпопулярніших технологій, розповів про їхню історію створення та основну функціональність. У другій частині я розповів про застосування Wrk і K6, в цій частині я розповім про використання Gatling і JMeter, а також підведу підсумки тестування.
У нас за останні роки накопичилося достатньо досвіду роботи з цими системами, і ми розглядаємо ці технології на деяких наших тренінгах. Але, зрозуміло, це настільки велика тема, що з кожної технології можна написати окрему статтю. Мені було цікаво порівняти можливості всіх чотирьох технологій. Сподіваюся, що ця стаття буде корисною всім, хто планує займатися тестуванням ефективності.
Gatling
Перейдемо до Gatling. З ним все складніше у плані автоматизації. Тут є Docker image для Enterprise версії, але тільки для корпоративних клієнтів. Є сторонній image, але, на жаль, перестав оновлюватися 4 роки тому. Тому доведеться писати власний gatling.dockerfile на основі JRE 20:
FROM eclipse-temurin:20-jre-alpine
WORKDIR /opt
ENV GATLING_VERSION 3.9.5
RUN mkdir -p gatling
RUN apk add --update wget bash libc6-compat && \
mkdir -p /tmp/downloads && \
wget -q -O /tmp/downloads/gatling-$GATLING_VERSION.zip \
https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/$GATLING_VERSION/gatling-charts-highcharts-bundle-$GATLING_VERSION-bundle.zip && \
mkdir -p /tmp/archive && cd /tmp/archive && \
unzip /tmp/downloads/gatling-$GATLING_VERSION.zip && \
mv /tmp/archive/gatling-charts-highcharts-bundle-$GATLING_VERSION/* /opt/gatling/ && \
rm -rf /tmp/*
WORKDIR /opt/gatling
ENV PATH /opt/gatling/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ENV GATLING_HOME /opt/gatling
ENTRYPOINT ["gatling.sh"]
Скрипти Gatling можна написати на Java/Scala/Kotlin. Скористаємося Java-варіантом і створимо клас UserSimulation:
public class UserSimulation extends Simulation {
HttpProtocolBuilder httpProtocol = http
.baseUrl("http://user:8080");
ScenarioBuilder scn = scenario("UserSimulation")
.exec(http("request")
.get("/benchmark"))
.pause(0, 3);
{
setUp(
scn.injectClosed(constantConcurrentUsers(Integer.getInteger("users")).during(Integer.getInteger("duration", 20)))
).protocols(httpProtocol);
}
}
Як ви бачите, єдина вимога — використання Simulation як базового класу. Всередині класу ми створюємо об’єкт HttpProtocolBuilder і вказуємо базовий URL для наших запитів, параметри запиту, різні необхідні заголовки і т.д.
Потім ми вказуємо відносний шлях (/benchmark) та знову паузу між запитами. Якщо вказати два граничні значення (0 і 3), то це означатиме, що Gatling буде щоразу використовувати для паузи випадкове число від 0 до 3 секунд.
У методі setup() ми вказуємо (впроваджуємо) наших користувачів, причому викликаємо метод injectOpen(). У Gatling є дві моделі контролю за створенням користувачів:
- Closed — коли кількість користувачів фіксована.
- Open — коли кількість користувачів може змінюватися згодом за різними правилами (throttle, ram-up, stress testing і т.д.).
Виберемо закриту модель з фіксованою кількістю користувачів (constantConcurrentUsers). І у методі during вкажемо тривалість тесту. Кількість одночасних користувачів та тривалість тестування беруться з системних змінних («users» та «duration»). Але як ми їх передаватимемо до цього класу?
На відміну від K6, де використовується JavaScript, в Gatling усі скрипти повинні бути відкомпільовані. Тому якби ми зараз запустили наш скрипт, то отримали б помилку:
germes-gatling-1 | Wrapped by: java.io.IOException: Cannot run program «javac» (in directory «/opt/gatling»): error=2, No such file or directory
Тому як базовий образ для Gatling контейнера потрібно вказувати JDK, а не JRE:
FROM eclipse-temurin:20-jdk-alpine
Головний скрипт в Gatling — gatling.sh (або gatling.bat). Саме йому ми і передаємо всі параметри:
gatling.sh -erjo «-Dusers=100 -Dduration=20» --
batch-mode --
run-mode local --
simulations-folder /opt/gatling/scripts/ --
simulation UserSimulation
Тут:
- -erjo — додаткові параметри для JVM (наші системні змінні);
--
batch-mode означає, що ми вимикаємо інтерактивний режим з різними питаннями та підтвердженнями для користувача;--
run-mode дозволяє вказати тип запуску (локальний, розподілений чи хмарний);--
simulations-folder вказує папку зі скриптами;--
simulation містить назву класу-скрипту, який ми хочемо запустити.
Шоста таблиця показує роботу Gatling для Spring MVC застосунку після прогріву.
Users | Latency (ms) | Req/sec | CPU, Gatling | CPU, App | Mem, Gatling | Mem, App |
100 | 2 | 60 | 45 | 32 | 374 | 331 |
1000 | 1 | 591 | 96 | 78 | 431 | 338 |
5000 | 11 | 2936 | 1548 | 18 | 566 | 550 |
Заради інтересу приберемо паузу з тесту та подивимося, як це позначиться на результатах. Це відразу призвело до великої кількості ConnectException на сервері (68% всіх запитів) і latency злетіло до 660 мс.
JMeter
Тепер перейдемо до останньої технології у цьому огляді — JMeter. Це єдина тулза, яка містить повноцінний UI на базі Java Swing, і це є істотною перевагою, тому що дає можливість використовувати її, не вдаючись до програмування. А це знижує поріг входження для застосування цієї технології.
Для роботи з JMeter потрібно спочатку запустити виконуваний файл (jmeter.bat/jmeter.sh) в режимі GUI (дефолтному), а потім створити свій тестовий план (Test Plan), тобто сценарій тестування вашого застосунку і потім зберегти в
Перший елемент — це Thread Group, де потрібно вказати два найважливіші параметри тесту — кількість користувачів і тривалість виконання тесту (duration). Але кількість користувачів змінюється від тесту до тесту ,та використовувати константу тут не вийде.
На щастя, у JMeter є properties. Ми можемо вказати значення ${__P(users)}, а потім передати значення цього property через опцію -J при старті JMeter (-Jusers=100). Ще один важливий параметр — ramp-up чи прогрів. Справа в тому, що JMeter запускається за допомогою JVM і їй самій потрібен деякий час, щоб запрацював JIT-компілятор, включилися певні оптимізації коду і т.д.
Наступний елемент — HTTP Request, який містить всю конфігурацію для того HTTP запиту, який JMeter надсилатиме на сервер:
- Host name/port.
- Відносний шлях та HTTP-метод.
- Тіло запиту (для запитів POST/PUT) та багато іншого.
Щоб переглянути результати тесту, додамо елемент Aggregate Graph. Тут можна або дивитися на результати тестування в режимі реального часу (якщо ви запустите тести через GUI), або завантажити файл результатів після.
А де ж паузи, які ми застосовували у K6/Gatling? Тут вони теж є, але у вигляді елемента Constant Timer, де ви вказуєте затримку в мілісекундах:
Ми не робитимемо цього, щоб не перевантажувати цей блок інформацією.
Тепер створимо Dockerfile для нового контейнера. На щастя, вже є базовий образ так, що потрібно лише його використовувати:
FROM justb4/jmeter
ADD docker-scripts/benchmark/jmeter/test.jmx /opt/
Команда для запуску тесту виглядає так:
jmeter -n -t /opt/test.jmx -l /opt/res.csv -e -Jusers=100
Тут ми вказали:
- опцію -n (batch режим без GUI). Такий режим рекомендований авторами JMeter, щоб GUI не впливала на продуктивність при тестуванні;
- -t (назва файлу);
- -e (генерувати report dashboard після тестування);
- — l (файл із результатами у форматі CSV);
- -J (properties).
Така команда використовується для локального запуску JMeter. Нам же потрібно прибрати jmeter з початку, тому що він і так автоматично додається до Docker image. Сьома таблиця показує роботу JMeter для Spring
Users | Latency (ms) | Req/sec | CPU, JMeter | CPU, App | Mem, JMeter | Mem, App |
100 | 2 | 42310 | 570 | 941 | 5030 | 573 |
1000 | 13 | 44681 | 586 | 928 | 5210 | 756 |
5000 | 27 | 41849 | 597 | 840 | 5730 | 871 |
Для запуску зі 100 користувачами файл результатів становив 86 мегабайт.
Висновки
Усі performance tools відпрацювали на відмінно, без помилок, але, зрозуміло, лише на рівні своїх можливостей. Приз за найпростіший інтерфейс і мінімальний поріг входження заслуговує Wrk. Це, по суті, утиліта командного рядка, яка дозволяє однією командою запустити load/ stress testing. Ще один її плюс — мінімальне споживання ресурсів:
- від 7 до 70 Мб пам’яті в самому піковому випадку;
300-330% завантаження CPU.
Її мінус — необхідність писати скрипти для налаштування запитів мовою Lua. Для найпростіших прикладів є готові скрипти, але для складніших доведеться хоч трохи знати Lua. Якогось просунутого готового DSL тут немає. У принципі, хоча Wrk і створили у нашому столітті, його мінімалізм дозволяє назвати його дитиною 20 століття. Він не підтримує ні розподіленого, ні хмарного, а лише локальне використання.
Тепер поговоримо про найненажерливішого учасника нашого експерименту — JMeter. Його споживання ресурсів зашкалює:
- мінімум 5 Гб пам’яті;
- завантаження CPU
570-600%.
Головний плюс JMeter — наявність GUI, де немає необхідності застосовувати навички програмування. А ще безліч підтримуваних протоколів і налаштувань, які помітно полегшують його використання. Це дуже стабільна технологія, думаю, що конфігураційні файли, створені
Завдяки своїй плагінній архітектурі він не застаріває, а навпаки, обростає все новими розширеннями. На поточний день ви можете скористатися понад 100 плагінами.
K6 та Gatling — два учасники, які, навпаки, інтенсивно розвиваються, і де ми писали скрипти для тестування. І в тому, і в іншому випадку у вас буде багатий DSL для налаштування ваших скриптів.
K6 використовує JavaScript, тому тут особливо немає необхідності в якомусь спеціальному IDE для роботи, тоді як Gatling скрипти вимагають Maven залежностей та IDE для того, щоб перевірити, щоб вони хоча б компілюються. Обидва вони підтримують хмарний режим роботи (у Gatling є Enterprise версія), а K6 — ще й розподілене тестування.
K6 завдяки розширенням може відправляти результати своєї роботи практично в будь-яку систему візуалізації, таку як Prometheus, Grafana, InfluxDB або New Relic. Gatling також має можливість інтеграції з деякими системами моніторингу, такими як Graphite, але тільки в платній версії Gatling Enterprise.
Але Gatling має зручний спосіб запуску через Maven/Gradle плагіни, які не вимагають його локальної інсталяції або Docker контейнера.
Що стосується споживання ресурсів, то у Gatling воно на диво помірне —
Ще одна важлива характеристика будь-якої технології — підтримка спільноти, наявність великої бази запитань та відповідей і документації. Якщо взяти Stackoverflow, то тут безумовним лідером є JMeter:
- JMeter — 18854 питання;
- Gatling — 1674;
- K6 — 305;
- Wrk — 41.
В цілому можна сказати, що написання performance-тестів не є тривіальним процесом і вимагає знання не тільки можливостей performance tools, але і сценаріїв роботи користувачів з вашим застосунком.
Підсумковий docker-compose-benchmark.yml виглядає так:
version : '3.8'
services:
wrk:
build:
context: .
dockerfile: docker-scripts/benchmark/wrk.dockerfile
command: wrk -t4 -c100 -d20s http://user:8080/benchmark
k6:
build:
context: .
dockerfile: docker-scripts/benchmark/k6/k6.dockerfile
command: run --vus 5000 --duration 20s /opt/test.js
gatling:
build:
context: .
dockerfile: docker-scripts/benchmark/gatling/gatling.dockerfile
command: gatling.sh -erjo "-Dusers=5000 -Dduration=20" --batch-mode --run-mode local --simulations-folder /opt/gatling/scripts/ --simulation UserSimulation
jmeter:
build:
context: .
dockerfile: docker-scripts/benchmark/jmeter/jmeter.dockerfile
command: -n -t /opt/test.jmx -l /opt/res.csv -e -Jusers=100
2 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів