Точное тестирование производительности с помощью `wrk`
API7.ai
November 25, 2022
В этой статье мы поговорим о тестировании производительности. Эта часть не уникальна для OpenResty, но применима и к другим серверным службам.
Тестирование производительности широко распространено, и когда мы поставляем продукты, они сопровождаются метриками производительности, такими как QPS, TPS, задержка, количество пользователей, поддерживаемых одновременно, и так далее. Для проектов с открытым исходным кодом мы также проводим тестирование производительности перед выпуском версии, чтобы сравнить её с предыдущей и убедиться, что нет значительного ухудшения. Существуют также нейтральные сайты, публикующие сравнительные данные производительности аналогичных продуктов. Должен сказать, что тестирование производительности близко к нам.
Итак, как проводить научное и строгое тестирование производительности?
Инструменты для тестирования производительности
Чтобы хорошо выполнить работу, сначала нужно использовать хороший инструмент. Выбор хорошего инструмента для тестирования производительности — это уже половина успеха.
Инструмент Apache Benchmark, также известный как ab, с которым вы, вероятно, знакомы, является, пожалуй, самым простым инструментом для тестирования производительности, но, к сожалению, он не очень полезен. Это связано с тем, что современные серверы разрабатываются на основе конкурентного и асинхронного ввода-вывода, производительность которых не так уж плоха. ab не использует преимущества многоядерности машины, и создаваемые запросы не оказывают достаточного давления. В этом случае результаты, полученные с помощью ab, не являются реальными.
Поэтому мы можем выбрать критерий для инструмента нагрузочного тестирования: сам инструмент должен иметь высокую производительность и быть способным генерировать достаточное давление, чтобы нагрузить серверную программу.
Конечно, вы также можете потратить больше денег, чтобы запустить множество клиентов для нагрузочного тестирования и превратить их в распределённую систему нагрузочного тестирования. Но не забывайте, что сложность при этом также возрастает.
Возвращаясь к практике OpenResty, наш рекомендуемый инструмент для тестирования производительности — это wrk. Почему мы выбираем его?
Во-первых, wrk соответствует критериям выбора инструмента. Нагрузка, создаваемая wrk на одной машине, может легко позволить NGINX достичь 100% загрузки процессора, не говоря уже о других серверных приложениях.
Во-вторых, wrk имеет много общего с OpenResty. wrk — это не проект с открытым исходным кодом, написанный с нуля; он стоит на плечах LuaJIT и Redis и использует преимущества многоядерных ресурсов системы для генерации запросов. Кроме того, wrk предоставляет Lua API, который позволяет вам встраивать свои Lua-скрипты для настройки заголовков и содержимого запросов, что делает его очень гибким.
Итак, как мы должны использовать wrk? Это так же просто, как посмотреть на следующий фрагмент кода.
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Это означает, что wrk будет использовать 12 потоков, удерживая 400 долгосрочных соединений в течение 30 секунд, чтобы отправлять HTTP-запросы на указанный API-интерфейс. Конечно, если вы не укажете параметры, wrk по умолчанию запустит два потока и десять долгосрочных соединений.
Тестовая среда
После того как мы нашли инструменты для тестирования, мы не можем сразу начать нагрузочное тестирование. Нам нужно проверить тестовую среду. В тестовой среде нужно проверить четыре основных пункта, и я подробно расскажу о них.
1. Отключение SELinux
Если у вас операционная система CentOS/RedHat, рекомендуется отключить SELinux. В противном случае вы можете столкнуться с множеством странных проблем с правами доступа.
Давайте проверим, включён ли SELinux, с помощью следующей команды.
$ sestatus SELinux status: disabled
Если он включён (enforcing), вы можете временно отключить его с помощью команды $ setenforce 0; также измените файл /etc/selinux/config, чтобы отключить его навсегда, изменив SELINUX=enforcing на SELINUX=disabled.
2. Максимальное количество открытых файлов
Затем вам нужно проверить текущее общее максимальное количество открытых файлов в системе с помощью следующей команды.
$ cat /proc/sys/fs/file-nr 3984 0 3255296
Последнее число 3255296 здесь — это максимальное количество открытых файлов. Если это число на вашей машине мало, вам нужно изменить файл /etc/sysctl.conf, чтобы увеличить его.
fs.file-max = 1020000 net.ipv4.ip_conntrack_max = 1020000 net.ipv4.netfilter.ip_conntrack_max = 1020000
После изменения необходимо перезапустить системную службу, чтобы изменения вступили в силу.
sudo sysctl -p /etc/sysctl.conf
3. Ограничения процессов
Помимо общего максимального количества открытых файлов в системе, существует также ограничение на количество файлов, которые может открыть процесс, которое вы можете проверить с помощью команды ulimit.
$ ulimit -n 1024
Вы заметите, что это значение по умолчанию равно 1024, что мало. Поскольку каждый пользовательский запрос соответствует файловому дескриптору, а нагрузочные тесты генерируют множество запросов, нам нужно увеличить это значение до миллионов, что вы можете временно сделать с помощью следующей команды.
ulimit -n 1024000
Вы также можете изменить конфигурационный файл /etc/security/limits.conf, чтобы сделать это изменение постоянным.
* hard nofile 1024000 * soft nofile 1024000
4. Конфигурация NGINX
Наконец, вам нужно внести небольшое изменение в конфигурацию NGINX, а именно следующие три строки кода.
events { worker_connections 10240; }
Это позволяет нам увеличить количество соединений на одного Worker. Поскольку значение по умолчанию составляет всего 512, этого недостаточно для высоконагруженных тестов.
Проверка перед нагрузочным тестированием
На этом этапе тестовая среда готова. Вы, наверное, уже хотите начать тестирование, верно? Давайте ещё раз проверим перед запуском теста с wrk. В конце концов, люди могут ошибаться, поэтому важно провести перекрёстную проверку.
Эта последняя проверка может быть разделена на два шага.
1. Использование автоматизированного инструмента c1000k
c1000k создан автором SSDB. Как видно из названия, цель этого инструмента — проверить, может ли ваша среда соответствовать требованиям 10^6 одновременных соединений.
Использование этого инструмента также очень простое. Мы запускаем server и client, соответствующие серверной программе, слушающей на порту 7000, и клиентской программе, запускающей нагрузочное тестирование, чтобы имитировать нагрузочное тестирование в реальной среде:
. /server 7000 . /client 127.0.0.1 7000
Сразу после этого client отправляет запрос на server, чтобы проверить, может ли текущая системная среда поддерживать один миллион одновременных соединений. Вы можете запустить это самостоятельно и увидеть результат.
2. Проверка, работает ли серверная программа нормально
Если серверная программа работает неправильно, нагрузочное тестирование может превратиться в тестирование обновления журнала ошибок или тестирование ответов 404.
Итак, последний и самый важный шаг тестирования тестовой среды — пройти через набор модульных тестов серверной программы или вручную вызвать несколько основных интерфейсов, чтобы убедиться, что все интерфейсы, возвраты и HTTP-коды ответов теста wrk нормальны и что в logs/error.log нет сообщений уровня ошибок.
Отправка запросов
Хорошо, теперь всё готово. Давайте начнём нагрузочное тестирование с wrk!
$ wrk -d 30 http://127.0.0.2:9080/hello Running 30s test @ http://127.0.0.2:9080/hello 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 595.39us 178.51us 22.24ms 90.63% Req/Sec 8.33k 642.91 9.46k 59.80% 499149 requests in 30.10s, 124.22MB read Requests/sec: 16582.76 Transfer/sec: 4.13MB
Я не указал параметры здесь, поэтому wrk по умолчанию запустит 2 потока и 10 долгосрочных соединений. Вам не нужно настраивать количество потоков и соединений в wrk на очень большое значение; достаточно, чтобы целевая программа достигла 100% загрузки процессора.
Но время нагрузочного тестирования не должно быть слишком коротким, так как несколько секунд нагрузочного тестирования бессмысленны. В противном случае нагрузочное тестирование, скорее всего, завершится до того, как серверная программа завершит горячую перезагрузку. В то же время вам нужно использовать инструмент мониторинга, такой как top или htop, чтобы проверить, работает ли целевая программа на сервере при 100% загрузке процессора во время нагрузочного тестирования.
Феноменально, если процессор полностью загружен, а использование процессора и памяти быстро снижается после остановки теста, то поздравляю, тест был успешно завершён. Однако, если есть какие-либо исключения, такие как следующие, как разработчик серверной части, вы должны обратить на них внимание.
- Процессор не может быть полностью загружен. Это не проблема
wrk; это может быть ограничение сети или блокирующая операция в вашем коде. Вы можете определить это, просмотрев свой код или используя flame graph дляoff CPU. - Процессор всегда полностью загружен, даже когда нагрузка прекращается. Это указывает на бесконечный цикл в коде, вызванный регулярным выражением или ошибкой LuaJIT, с которой я сталкивался в реальных средах. В этот момент вам нужно будет использовать flame graph для процессора, чтобы определить это.
Наконец, давайте посмотрим на статистику wrk. В отношении этого результата мы обычно обращаем внимание на два значения.
Первое — это QPS, или Requests/sec: 16582.76, что является точной цифрой, показывающей, сколько запросов обрабатывается на серверной стороне в секунду.
Второе — это задержка: Latency 595.39us 178.51us 22.24ms 90.63%, которая так же важна, как и QPS, и отражает скорость отклика системы. Например, для шлюзовых приложений мы хотим, чтобы задержка оставалась в пределах 1 мс.
Кроме того, wrk также предоставляет параметр latency, который подробно выводит процентное распределение задержек, например.
Latency Distribution 50% 134.00us 75% 180.00us 90% 247.00us 99% 552.00us
Однако данные о распределении задержек wrk неточны, поскольку они искусственно добавляют сетевые и инструментальные возмущения, которые усиливают задержку, что требует вашего особого внимания.
Заключение
Тестирование производительности — это техническая работа; не многие могут сделать её правильно и хорошо. Я надеюсь, что эта статья даст вам более полное понимание тестирования производительности.
Наконец, я оставлю вам вопрос: wrk поддерживает пользовательские Lua-скрипты для нагрузочного тестирования, так сможете ли вы написать простой Lua-скрипт на основе его документации? Это может быть немного сложно, но вы поймёте намерения интерфейсов, предоставляемых wrk, когда закончите.
Вы можете поделиться этой статьёй с большим количеством людей, и мы будем двигаться вперёд вместе.