Данная статья является переводом статьи Кевина Голдберга «A Performance Analysis of Python WSGI Servers: Part 2» dzone.com/articles/a-performance-analysis-of-python-wsgi-servers-part с небольшими дополнениями от переводчика.
Введение
В первой части этой серии Вы познакомились с WSGI и с шестью наиболее популярными по мнению автора WSGI-серверами. В этой части Вам будет показан результат анализа производительности этих серверов. С этой целью была создана специальная тестовая песочница.
Конкурсанты
Из-за нехватки времени исследование было ограничено шестью WSGI-серверами. Весь код с инструкциями по запуску для этого проекта размещен на GitHub. Возможно со временем проект будет расширяться и будут представлены анализы производительности для других WSGI-серверов. Но пока речь пойдёт о шести серверах:
- Bjoern описывает себя как « сверхбыстрый WSGI-сервер» и может похвастаться тем, что это «самый быстрый, самый маленький и легкий WSGI-сервер». Мы создали небольшое приложение, использующее большинство параметров библиотеки по умолчанию.
- CherryPy - чрезвычайно популярный и стабильный фреймворк и WSGI-сервер. Этот небольшой скрипт использовался для обслуживания нашего образца приложения через CherryPy.
- Gunicorn был вдохновлен сервером Unicorn от Ruby (отсюда и название). Он скромно утверждает, что он «просто реализован, легок в использовании и довольно быстрый». В отличие от Bjoern и CherryPy, Gunicorn является автономным сервером. Мы создали его с помощью этой команды. Параметр «WORKER_COUNT» был установлен в два раза больше доступных ядер процессора, плюс один. Это было сделано на основании рекомендаций из документации Gunicorn.
- Meinheld - это высокопроизводительный WSGI-совместимый веб-сервер, который утверждает, что он легкий. Основываясь на примере, указанном на сайте сервера, мы создали своё приложение.
- mod_wsgi создан тем же создателем, что и mod_python. Подобно mod_python, он доступен только для Apache. Однако он включает инструмент под названием «mod_wsgi express», который создаёт минимально возможный инстанс Apache. Мы сконфигурировали и использовали mod_wsgi express с помощью этой команды. Чтобы соответствовать Gunicorn, мы настроили mod_wsgi т.о, чтобы создать worker-ов вдвое больше чем ядер процессора.
- uWSGI - полнофункциональный сервер приложений. Как правило, uWSGI сопрягается с прокси-сервером (например: Nginx). Однако, чтобы лучше оценить производительность каждого сервера, мы попытались использовать только голые серверы и создали двух worker-ов для каждого доступного ядра процессора.
Бенчмарк
Чтобы сделать тест максимально объективным, был создан Docker-контейнер для изоляции тестируемого сервера от остальной части системы. Также использование Docker-контейнера гарантировало, что каждый запуск начинается с чистого листа.
Сервер:
- Изолирован в Docker-контейнере.
- Выделено 2 ядра процессора.
- Оперативная память контейнера была ограничена до 512 МБ.
Тестирование:
- wrk, современный инструмент HTTP-бенчмаркинга, запускал тесты.
- Серверы были протестированы в произвольном порядке с увеличением числа одновременных соединений в диапазоне от 100 до 10000.
- wrk был ограничен двумя ядрами ЦПУ, не используемыми Docker.
- Каждый тест длился 30 секунд и повторялся 4 раза.
Метрика:
- Среднее количество постоянных запросов, ошибок и задержек предоставлялось wrk.
- Встроенный в Docker, мониторинг показывал уровни использования ЦПУ и ОЗУ.
- Самые высокие и самые низкие показания были отброшены, а остальные значения были усреднены.
- Для любопытных мы отправили полный скрипт на GitHub.
Результаты
Все исходные показатели производительности были включены в репозиторий проекта, а также предоставлен сводный CSV-файл. Также для визуализации были созданы графики в среде Google-doc.
Зависимость RPS от числа одновременных соединенинй
На этом графике показано среднее количество одновременных запросов; Чем выше число, тем лучше.
- Bjoern: Явный победитель.
- CherryPy: Несмотря на то, что он написан на чистом Python, он был лучшим исполнителем.
- Meinheld: Отличные показатели, учитывая скудные ресурсы контейнера.
- mod_wsgi: Не самый быстрый, но производительность была последовательной и адекватной.
- Gunicorn: Хорошая производительность при более низких нагрузках, но прослеживается борьба при большом количестве соединений.
- uWSGI: Разочаровал плохими результатами.
ПОБЕДИТЕЛЬ: Bjoern
Bjoern
По количеству постоянных запросов Bjoern является очевидным победителем. Однако, учитывая, что цифры намного выше, чем у конкурентов, мы немного скептически настроены. Мы не уверены в том, что Bjoern действительно настолько ошеломляюще быстрый. Сначала мы тестировали серверы по алфавиту, и мы думали, что Bjoern получил несправедливое преимущество. Однако даже после запуска серверов в произвольном порядке сервера и повторного тестирования результат остался прежним.
uWSGI
Мы были разочарованы слабыми результатами uWSGI. Мы ожидали, что он окажется в лидерах. Во время тестирования мы заметили, что логи uWSGI печатает на экране, и первоначально мы объяснили отсутствие производительности дополнительной работой, которую выполнял сервер. Тем не менее, даже после добавленной опции «--disable-logging», uWSGI по-прежнему является самым медленным сервером.
Как упоминалось в руководстве uWSGI, оно обычно сопрягается с прокси-сервером, таким как Nginx. Однако мы не уверены, что это может объяснить такую большую разницу.
Задержка
Задержка — это количество времени, прошедшего между запросом и его ответом. Более низкие цифры — лучше.
- CherryPy: Хорошо справлялся с нагрузкой.
- Bjoern: В целом низкие задержки, но лучше работает при меньшем количестве одновременных соединений.
- Gunicorn: хорош и последователен.
- mod_wsgi: Средняя производительность, даже при большом количестве одновременных соединений.
- Meinheld: В целом, приемлемая производительность.
- uWSGI: uWSGI снова на последнем месте.
ПОБЕДИТЕЛЬ: CherryPy
Использование ОЗУ
Эта метрика показывает требования к памяти и «легкость» каждого сервера. Более низкие цифры — лучше.
- Bjoern: Чрезвычайно легкий. Использует всего лишь 9 МБ ОЗУ для обработки 10 000 одновременных запросов.
- Meinheld: Такой же как Bjoern.
- Gunicorn: Умело справляется с высокими нагрузками с едва заметным потреблением памяти.
- CherryPy: Первоначально нуждался в небольшом количестве оперативной памяти, но её использование стремительно увеличивалось с ростом нагрузки.
- mod_wsgi: На более низких уровнях он был одним из наиболее интенсивных в памяти, но оставался довольно последовательным.
- uWSGI: Очевидно, что у тестируемой нами версии проблемы с количеством потребляемой памяти.
ПОБЕДИТЕЛИ: Bjoern и Meinheld
Количество ошибок
Ошибка возникает, когда сервер падает, прерывается или истекает время запроса. Чем ниже, тем лучше.
Для каждого сервера мы рассчитали отношение общее отношение количества запросов к числу ошибок:
- CherryPy: коэффициент ошибок около 0, даже при высоком количестве соединений.
- Bjoern: Ошибки встречались, но это компенсировалось количеством обработанных запросов.
- mod_wsgi: Хорошо работает с приемлемой частотой ошибок 6%.
- Gunicorn: Работает с 9-процентной частотой ошибок.
- uWSGI: Учитывая низкое количество запросов, которые он обслуживал, он оказался с 34-процентной частотой ошибок.
- Meinheld: Упал на более высоких нагрузках, выбросив более 10 000 ошибок во время самого требовательного теста.
ПОБЕДИТЕЛЬ: CherryPy
Использование ЦПУ
Высокое использование ЦПУ не является хорошим или плохим, если сервер работает хорошо. Однако, это даёт некоторые интересные сведения о работе сервера. Поскольку использовались два ядра ЦПУ, максимальное возможное использование составляет 200 процентов.
- Bjoern: однопоточный сервер, о чем свидетельствует его последовательное использование на 100% ЦПУ.
- CherryPy: многопоточный, но застрял на 150-ти процентах. Это может быть связано с GIL Python.
- Gunicorn: использует несколько процессов с полным использованием ресурсов ЦПУ на более низких уровнях.
- Meinheld: однопоточный сервер, использующий ресурсы ЦПУ как Bjoern.
- mod_wsgi: Многопоточный сервер использующий все ядра ЦПУ на протяжении всех измерений
- uWSGI: очень низкое использование ЦПУ. Потребление ресурсов ЦПУ не превышает 50-ти процентов. Это одно из доказательств того, что uWSGI неправильно сконфигурирован.
ПОБЕДИТЕЛЬ: Нет, поскольку это скорее наблюдение в поведении, чем сравнение в производительности.
Заключение
Подведем итог! Вот некоторые общие идеи, которые можно почерпнуть из результатов каждого сервера:
- Bjoern: Оправдывает себя как «супербыстрый, ультралегкий WSGI-сервер».
- CherryPy: Высокая производительность, маленькое потребление памяти и маленькое количество ошибок. Неплохо для чистого Python.
- Gunicorn: Хороший сервер для средних нагрузок.
- Meinheld: Хорошо работает и требует минимальных ресурсов. Тем не менее, борется с более высокими нагрузками.
- mod_wsgi: Интегрируется в Apache и отлично работает.
- uWSGI: Очень разочаровал. Либо мы неправильно сконфигурировали uWSGI, либо версия, которую мы установили, имеет базовые ошибки.
Автор: Степан Филатов