Подтвердить что ты не робот

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

ведут себя непредсказуемо в многопроцессорной среде?

Спасибо!

4b9b3361

Ответ 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.