Сами AÏT-OUAKLI и Anne-Sophie TANGUY - стажеры в научно-исследовательской лаборатории EPITECH для студентов-первокурсников и студентов третьего курса EPITECH. Их работа вращается вокруг исследования и разработки проектов, предлагаемых различными продуктовыми командами vente-privee.

Бутылочные серверы WSGI и сокеты Unix в Python

Что, если бы вы использовали серверы Bottle с сокетом Unix? Сумасшедший, скажете вы. Теперь предположим, что вы хотели увеличить производительность этих серверов. Что ж, вы можете захотеть инкапсулировать их, чтобы сделать что-то лучше, которое могло бы обрабатывать больше запросов в секунду. Если найденное вами решение требует, чтобы серверы WSGI от Bottle запускались с сокетами Unix, вы попали в нужное место.

Серверы обработки Mono

Мы испробовали для вас несколько серверов, которые работают с бутылкой. Тесты проводились с помощью тестовой среды Locust. Locust отправляет 1000 пользователей, моделируя ожидание от 500 мс до 900 мс между выполнением задач для каждого пользователя.

Бьорн и Мейнхельд являются асинхронными серверами. Однако Cherrypy и Waitress многопоточны.

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

При мониторинге использования ЦП на серверах мы заметили, что использовалось только одно ядро ​​(и на полную мощность), хотя сервер был многопоточным. Как ни странно, многопоточные серверы (Cherrypy и Waitress в предыдущем примере) даже медленнее, чем другие. Это из-за GIL.

Вы слышали о GIL? Глобальная блокировка интерпретатора python, именно она вызывает у вас столько проблем с вашими потоками. Это механизм, используемый в Python для синхронизации выполнения потоков, так что одновременно может выполняться только один собственный поток, что значительно снижает производительность потоков в многоядерных системах.

Сначала попробуйте использовать сокеты TCP / IP

Мы подумали, что если проблема возникла с GIL, тогда нам следует обратиться к программе, которая инкапсулирует эти серверы и запускает многие из них благодаря многопроцессорности, но мы увидим эту часть позже.

Для этой программы мы хотели действовать как прокси: получать запросы от клиентов, отправлять их на серверы, получать их ответы и отправлять их обратно клиентам. Чтобы действовать таким образом, нам нужно было установить связь между нашей программой и серверами.

Сначала мы выбрали сокет TCP / IP. Таким образом, мы могли сделать нашу программу переносимой. Но это может привести к тому, что люди получат доступ к этому сокету извне и нарушат отправку запросов.

Вторая попытка с сокетами Unix

Мы поняли, что с самого начала следовало использовать сокеты Unix вместо TCP / IP. Здесь вы говорите себе: конечно, глупо. Вы должны были сделать это с самого начала! В этой части вы не общаетесь по сети.

Но все же, почему сокеты Unix лучше, чем сокеты TCP / IP? Сокет Unix - это межпроцессный механизм, который обеспечивает двунаправленный обмен данными между процессами, запущенными на одном компьютере. Сокеты домена UNIX используют файловую систему в качестве адресного пространства имен. Это означает, что вы можете использовать права доступа к файлам UNIX для управления доступом для связи с ними. В то время как сокеты TCP / IP зацикливаются на соединениях localhost. Короче говоря, сокеты Unix будут иметь меньше копий данных и меньше переключателей контекста, поэтому сокеты Unix должны иметь лучшую производительность в целом.

А теперь самое интересное: использование WSGI-серверов бутылки с сокетами Unix.

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

Вот несколько примеров того, как эти серверы запускаются из сокета Unix. Некоторые из них позволяют добавлять параметры с помощью функций run () или server (), поэтому мы оставим исследование их конкретных вариантов вам.

Мы рассматриваем «socket_path» как ваш путь к сокету UNIX (простая строка), а «wsgi_application» - как ваше приложение WSGI.

Вот простое приложение WSGI Hello World, чтобы вы могли протестировать его самостоятельно:

Мейнхельд (асинхронный):

Лучше закодируйте свой «socket_path», если вы не хотите столкнуться с ошибкой «TypeError: args tuple or string (path)».

Ссылка на GitHub

Ссылка на документацию

Официантка (многопоточная):

Ссылка на GitHub

Ссылка на документацию

Cherrypy (многопоточный):

Эта вишня все еще использует приложения WSGI от бутылки. Версия cherrypy, которую мы использовали для этого примера, - это cherrypy 8.9.1.

Ссылка на GitHub (это последняя версия Cherrypy)

Ссылка на документацию

Cheroot (многопоточный):

Ссылка на GitHub

Ссылка на документацию

Gunicorn (многопроцессорный):

С Gunicorn вам понадобится класс, унаследовавший класс своего приложения как такового. Обработчик настроен для вашего приложения, а словарь конфигурации настроен с ключом «bind» и значением «unix:», добавленным к пути к вашему сокету. После инкапсуляции этого класса вы можете запустить его с помощью метода run.

Ссылка на GitHub

Ссылка на документацию

Бьорн (асинхронный):

Ссылка на GitHub

Eventlet (асинхронный):

Ссылка на GitHub

Ссылка на документацию

Торнадо (асинхронный):

Ссылка на GitHub

Ссылка на документацию

Скрученный (асинхронный):

Twisted заставит вас создать и запустить свой пул потоков. Как это грубо. С его помощью вы добавите прослушиватель событий, чтобы пул останавливался при срабатывании. Затем вам нужно создать фабрику с вашим приложением WSGI, используя listenUNIX с вашим путем к сокету и вновь созданной фабрикой, и, наконец, запустить сервер с его методом run.

Ссылка на GitHub

Ссылка на документацию

Улучшения многопроцессорности

После настройки всех этих серверов в вашем проекте вы можете столкнуться с новой проблемой. Допустим, вы хотите подключить несколько экземпляров одного и того же сервера к одному сокету Unix, сначала вы скажете: неважно, сокет может обрабатывать несколько подключений одновременно. Если только эти серверы не попытаются создать указанный сокет и несколько раз привязаться к одному и тому же сокету Unix. Это вызовет ошибку привязки. Для них вам понадобится один сокет Unix на каждый экземпляр сервера. Для других, которые проверяют, связаны ли они уже с сокетом Unix, вы не столкнетесь с этой проблемой.

Вот список тех, которые доставят вам неприятности:
Bjoern, Eventlet, Tornado и Twisted.

Обратите внимание, что использовать Twisted с многопоточностью не так просто, как другие.
Фактически, Twisted имеет свой собственный управляемый событиями способ запуска подпроцессов. Вам нужно будет использовать spawnProcess API для обработки вывода подпроцессов. Если вы придерживаетесь многопроцессорной библиотеки Python, вам нужно будет разработать собственный способ получения вывода из подпроцесса Twisted на основе очередей.

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

А затем запустить его дочерние процессы

Вот результат, когда Meinheld работал на Terpan по сравнению с Meinheld, оба работали на бутылке:

На этот раз locust запустил 7000 пользователей, каждый из которых ждал от 500 до 900 мсек перед отправкой запроса:

Как видите, нам удалось получить намного больше запросов в секунду, чем в одиночку Meinheld, благодаря многопроцессорности. Обратите внимание, что Terpan может использовать всю мощность компьютера, потому что он использует все ядра по сравнению с Meinheld, используя всю емкость одного ядра, что дает Terpan возможность масштабироваться по горизонтали за счет увеличения количества ядер.

Некоторые серверы не имеют возможности подключиться к сокету Unix. Однако, если вы найдете больше, не стесняйтесь поделиться этим в комментариях ниже.

Мы надеемся, что это сэкономило вам время на поиск или дало вам несколько идей.

_________

Источники:

Автор: Айт ОУАКЛИ САМИ и Энн-Софи Танги