Вопрос или проблема
Я пишу сервер агрегирования событий на Python, используя Nginx + Gunicorn. Система масштабируется до примерно 300 запросов в секунду, прежде чем процессор достигает максимума на 1 CPU/2 ядра (AWS c4.large). Добавление дополнительного рабочего процесса Gunicorn или использование рабочих процессов с eventlet помогает только по краям (примерно 10%). Ответы находятся в диапазоне 1-2 мс (события записываются на диск).
Согласно моему анализу, Gunicorn тратит все своё время и энергию на чтение из сокета (это происходит в sync.py в вызове select()). Тем временем Nginx занимает около 2-3% процессора. Использование UNIX сокетов не изменило профиль производительности по сравнению с сетевыми сокетами.
Поскольку эти события такие маленькие (~ 200 байт), оказывается, что Gunicorn требует чрезмерного количества усилий, чтобы получить запрос из сокета. Я бы хотел иметь возможность каким-то образом объединить эти нагрузочные пакеты, прежде чем они достигнут Gunicorn, но я не знаю, как это сделать. Есть ли способ уменьшить количество усилий/процессорного времени, которое тратит Gunicorn, и увеличить мою пропускную способность на каждую машину?
Может быть,
https://medium.com/building-the-system/gunicorn-3-means-of-concurrency-efbb547674b7
поможет
попробуйте разные значения для --workers
.
Ответ или решение
Для оптимизации использования CPU Gunicorn при обработке малых запросов можно предпринять несколько подходов. Ваши текущие параметры предполагают использование Nginx в качестве обратного прокси и Gunicorn для обработки процессов, с загруженностью CPU, достигающей предела при 300 RPS на экземпляре с конфигурацией 1 CPU / 2 Cores (AWS c4.large). Это говорит о необходимости более эффективного распределения нагрузки.
Анализ производительности Gunicorn
Gunicorn тратит значительную часть ресурсов на чтение данных с сокета, что подтверждается местоположением в sync.py
в вызове select()
. Это свидетельствует о том, что проблема, вероятно, кроется в неэффективности обработки множества небольших запросов объемом около 200 байт.
Стратегии оптимизации
-
Увеличение количества рабочих процессов (workers):
Попробуйте варьировать количество рабочих процессов с помощью параметра--workers
. Но учтите, что из-за ограниченного числа ядер значительное увеличение приведет к контекстным переключениям и может усугубить проблему. -
Использование асинхронных рабочих процессов:
Рассмотрите использованиеgevent
илиeventlet
для улучшения справляемости с I/O-операциями. Асинхронные решения могут повысить производительность за счет неблокирующего ввода-вывода. -
Предварительная агрегация запросов:
Если возможно, настройте Nginx для агрегации нескольких мелких запросов перед их передачей Gunicorn. Это сложная задача, требующая изменения архитектуры, но может существенно снизить нагрузку. -
Тюнинг Nginx:
Проверьте конфигурацию Nginx на предмет оптимизации использования CPU, например, за счет настройки buffer size и других параметров, влияющих на передачу запросов. -
Другие конфигурационные улучшения:
- Используйте оперативную память максимально эффективно, тюнингуйте настройки Gunicorn, такие как
worker_connections
, которые регулируют количество соединений на одного работника. - Оптимизируйте конфигурации виртуального окружения (также известного как virtual environment), контролируя версии используемых библиотек для уменьшения зависимости от ресурсов.
- Используйте оперативную память максимально эффективно, тюнингуйте настройки Gunicorn, такие как
-
Профилирование приложения:
Произведите профилирование вашего приложения, чтобы выявить узкие места, которые могут быть неочевидны. Данное действие может выявить менее очевидные проблемы, связанные с кодом или библиотеками.
Заключение
Чтобы решить проблему высокой нагрузки на CPU при использовании Gunicorn, ключевыми являются балансировка между числом рабочих процессов, выбор способов асинхронной обработки и архитектурная оптимизация. Изучение и тестирование различных конфигураций помогут найти наиболее оптимальное решение для вашей задачи. Подход планирования и имплементации, основанный на исследовании и анализе, обеспечит надлежащее распределение загрузки и повышение производительности.
Этот подход не только снизит нагрузку на CPU, но и улучшит общую производительность вашего сервера, позволив увеличить пропускную способность и, возможно, снизить затраты на инфраструктуру.