Python, WSGI, многопроцессорность и общие данные
Я немного запутался в функции мультипроцесса mod_wsgi и об общем дизайне WSGI-приложений, которые будут выполняться на серверах WSGI с возможностью многопроцессорности.
Рассмотрим следующую директиву:
WSGIDaemonProcess example processes=5 threads=1
Если я правильно понимаю, mod_wsgi будет порождать 5 процессов Python (например, CPython), и любой из этих процессов может получить запрос от пользователя.
В документации указано, что:
Если общие данные должны быть видимыми для всех экземпляров приложения, независимо от того, какой дочерний процесс они выполняются, и изменений, внесенных в данные одним приложением немедленно доступны для другого, включая любое выполнение в другом дочернем процессе, внешние данные таких как база данных или разделяемая память. Глобальный переменные в обычных модулях Python не могут использоваться для этой цели.
Но в этом случае становится очень тяжело, когда нужно быть уверенным, что приложение работает в любых условиях WSGI (включая многопроцессорные).
Например, простая переменная, которая содержит текущее количество подключенных пользователей, должна ли она быть безопасна в процессе чтения/записи из/в memcached или в БД или (если такие механизмы вне стандартной библиотеки доступная) общая память?
И будет ли код вроде
counter = 0
@app.route('/login')
def login():
...
counter += 1
...
@app.route('/logout')
def logout():
...
counter -= 1
...
@app.route('/show_users_count')
def show_users_count():
return counter
ведут себя непредсказуемо в многопроцессорной среде?
Спасибо!
Ответы
Ответ 1
В вашем вопросе есть несколько аспектов.
Во-первых, взаимодействие между apache MPM и приложениями mod_wsgi. Если вы запустили приложение mod_wsgi во встроенном режиме (нет WSGIDaemonProcess
, WSGIProcessGroup %{GLOBAL}
), вы наследуете многопроцессорность/многопоточность из MPM Apache. Это должен быть самый быстрый вариант, и в конечном итоге у вас есть несколько процессов и несколько потоков на процесс, в зависимости от вашей конфигурации MPM. Напротив, если вы запустили mod_wsgi в режиме демона, с WSGIDaemonProcess <name> [options]
и WSGIProcessGroup <name>
, у вас есть тонкий контроль над многопроцессорной/многопоточной обработкой за счет небольшой служебной информации.
Внутри одного сервера apache2 вы можете определить ноль, один или несколько названных WSGIDaemonProcess
es, и каждое приложение может быть запущено в одном из этих процессов (WSGIProcessGroup <name>
) или запускаться во встроенном режиме с помощью WSGIProcessGroup %{GLOBAL}
.
Вы можете проверить многопроцессорность/многопоточность, проверив переменные wsgi.multithread
и wsgi.multiprocess
.
С вашей конфигурацией WSGIDaemonProcess example processes=5 threads=1
у вас есть 5 независимых процессов, каждый из которых имеет один поток выполнения: никаких глобальных данных, нет разделяемой памяти, поскольку вы не контролируете подпроцессы нереста, но mod_wsgi делает это за вас. Чтобы поделиться глобальным состоянием, вы уже указали некоторые возможные варианты: БД, с которой взаимодействует ваш процесс, какая-то стойкость на основе файловой системы, процесс демона (запущен за пределами apache) и IPC на основе сокетов.
Как отметил Роланд Смит, последний может быть реализован с использованием API высокого уровня multiprocessing.managers
: внешний apache, который вы создаете и запускаете сервер BaseManager
m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.get_server().serve_forever()
и внутри вас приложения connect
:
m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.connect()
Приведенный выше пример является фиктивным, так как m
не имеет зарегистрированного полезного метода, но здесь (python docs) вы найдете, как создать и прокси-объект для ваших объектов (например, counter
в вашем примере).
Последний комментарий к вашему примеру, с processes=5 threads=1
. Я понимаю, что это всего лишь пример, но в реальных приложениях я подозреваю, что производительность будет сопоставимой по сравнению с processes=1 threads=5
: вы должны войти в тонкости совместного использования данных в многопроцессорной обработке, только если ожидаемое повышение производительности над "единственным процессом" значительная модель многих потоков.
Ответ 2
В документах по процессам и потокам для wsgi:
Когда Apache запускается в режиме, в котором есть несколько дочерних процессов, каждый дочерний процесс будет содержать вспомогательные интерпретаторы для каждого приложения WSGI.
Это означает, что в вашей конфигурации 5 процессов с 1 потоком каждый будут содержать 5 интерпретаторов и не будут иметь общих данных. Ваш встречный объект будет уникальным для каждого интерпретатора. Вам нужно будет либо создать какое-то настраиваемое решение для подсчета сеансов (один общий процесс, с которым вы можете общаться, какое-то решение на основе устойчивости, и т.д.) ИЛИ, и это определенно моя рекомендация, используйте предварительно построенное решение (Google Analytics и Chartbeat фантастические варианты).
Я склонен думать о том, чтобы использовать глобальные переменные для обмена данными как большой формы глобального злоупотребления. Это ошибка и проблема переносимости в большинстве сред, в которых я выполнял параллельную обработку. Что делать, если вдруг ваше приложение должно запускаться на нескольких виртуальных машинах? Это нарушит ваш код независимо от того, какая модель обмена потоками и процессами.
Ответ 3
Если вы используете multiprocessing
, несколько способов делиться данными между процессами. Значения и Массивы работают только в том случае, если процессы имеют родительский/дочерний (они разделяются путем наследования). Если это не так, используйте Manager
и Proxy
.