python multiprocessing mod-wsgi wsgi

Python, WSGI, multiprocesamiento y datos compartidos.



multiprocessing mod-wsgi (3)

De los documentos sobre procesos y subprocesos para wsgi:

Cuando Apache se ejecuta en un modo en el que hay varios procesos secundarios, cada proceso secundario contendrá subprocesos para cada aplicación WSGI.

Esto significa que en su configuración, 5 procesos con 1 subproceso cada uno, habrá 5 intérpretes y no se compartirán datos. Su objeto contador será único para cada intérprete. Necesitará crear una solución personalizada para contar las sesiones (un proceso común con el que se puede comunicar, algún tipo de solución basada en la persistencia, etc.) O, y esta es definitivamente mi recomendación, use una solución precompilada (Google Analytics y Chartbeat son opciones fantásticas).

Tiendo a pensar en usar globales para compartir datos como una gran forma de abuso global. Es un problema de errores y de portabilidad en la mayoría de los entornos en los que he realizado el procesamiento paralelo. ¿Qué pasaría si de repente su aplicación se ejecutara en varias máquinas virtuales? Esto rompería su código sin importar cuál sea el modelo compartido de subprocesos y procesos.

Estoy un poco confundido sobre la función de multiprocesamiento de mod_wsgi y sobre un diseño general de aplicaciones WSGI que se ejecutaría en servidores WSGI con capacidad de multiprocesamiento.

Considere la siguiente directiva:

WSGIDaemonProcess example processes=5 threads=1

Si entiendo correctamente, mod_wsgi generará 5 procesos de Python (por ejemplo, CPython) y cualquiera de estos procesos puede recibir una solicitud de un usuario.

La documentación dice que:

Donde los datos compartidos deben ser visibles para todas las instancias de la aplicación, independientemente del proceso secundario en el que se ejecuten, y los cambios realizados en los datos por una aplicación están disponibles de inmediato para otra, incluyendo cualquier ejecución en otro proceso secundario, un almacén de datos externo como Se debe utilizar una base de datos o memoria compartida. Las variables globales en los módulos normales de Python no pueden usarse para este propósito.

Pero en ese caso se vuelve muy pesado cuando uno quiere asegurarse de que una aplicación se ejecuta en cualquier condición WSGI (incluidas las de multiprocesamiento).

Por ejemplo, una variable simple que contiene la cantidad actual de usuarios conectados, en caso de que se pueda leer / escribir desde / a memcached a prueba de procesos, o una base de datos o (si se dispone de mecanismos de bibliotecas fuera de lo estándar) ¿memoria?

Y le gustará el código

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

¿Se comporta de forma impredecible en un entorno de multiprocesamiento?

¡Gracias!


Hay varios aspectos a considerar en su pregunta.

Primero, la interacción entre las aplicaciones apache MPM y mod_wsgi. Si ejecuta la aplicación mod_wsgi en modo incrustado (no se necesita WSGIDaemonProcess , WSGIProcessGroup %{GLOBAL} ), hereda el multiprocesamiento / multiproceso de los MPM de apache. Esta debería ser la opción más rápida y terminará teniendo múltiples procesos y múltiples subprocesos por proceso, dependiendo de su configuración de MPM. Por el contrario, si ejecuta mod_wsgi en modo daemon, con WSGIDaemonProcess <name> [options] y WSGIProcessGroup <name> , tiene un control preciso sobre el multiproceso / multiproceso a costa de una pequeña overhead .

Dentro de un único servidor apache2 puede definir cero, uno o más WSGIDaemonProcess es, y cada aplicación puede ejecutarse en uno de estos procesos ( WSGIProcessGroup <name> ) o ejecutarse en modo integrado con WSGIProcessGroup %{GLOBAL} .

Puede verificar el multiprocesamiento / multiproceso inspeccionando las variables wsgi.multithread y wsgi.multiprocess .

Con su configuración, WSGIDaemonProcess example processes=5 threads=1 , tiene 5 procesos independientes, cada uno con un único subproceso de ejecución: no hay datos globales, no hay memoria compartida, ya que no tiene el control de los subprocesos de desove, pero mod_wsgi lo hace por usted. . Para compartir un estado global, ya enumeró algunas opciones posibles: una base de datos con la que interactúan sus procesos, algún tipo de persistencia basada en el sistema de archivos, un proceso de daemon (iniciado fuera de Apache) y IPC basado en sockets.

Como señaló Roland Smith, este último podría implementarse utilizando una API de alto nivel mediante multiprocessing.managers : fuera de apache, usted crea e inicia un proceso de servidor BaseManager

m = multiprocessing.managers.BaseManager(address=('''', 12345), authkey=''secret'') m.get_server().serve_forever()

y dentro de ti aplicaciones connect :

m = multiprocessing.managers.BaseManager(address=('''', 12345), authkey=''secret'') m.connect()

El ejemplo anterior es ficticio, ya que m no tiene un método útil registrado, pero here (documentos de Python) encontrará cómo crear y representar un objeto (como el counter en su ejemplo) entre sus procesos.

Un comentario final sobre su ejemplo, con processes=5 threads=1 . Entiendo que esto es solo un ejemplo, pero en las aplicaciones del mundo real, sospecho que el rendimiento será comparable con respecto a los processes=1 threads=5 : debe entrar en las complejidades de compartir datos en multiproceso solo si el aumento de rendimiento esperado sobre el El modelo de ''proceso único muchos hilos'' es significativo.


Si está utilizando multiprocessing , hay varias formas de compartir datos entre procesos. Values y las Arrays solo funcionan si los procesos tienen una relación padre / hijo (se comparten al heredar). Si ese no es el caso, use un multiprocessing.managers y objetos Proxy .