Python, WSGI, multiprocessing et données partagées
je suis un peu confus au sujet de la fonctionnalité de multipressing de mod_wsgi et au sujet d'une conception générale des applications WSGI qui seraient exécutées sur des serveurs WSGI avec la capacité de multipressing.
Envisager la directive suivante:
WSGIDaemonProcess example processes=5 threads=1
si je comprends bien, mod_wsgi va générer 5 processus Python (par exemple CPython) et l'un de ces processus peut recevoir une requête d'un utilisateur.
La documentation dit que:
Lorsqu'ils sont partagés les données doivent être visibles pour toutes les instances d'application, quel que soit le processus enfant qu'elles exécutent, et les changements apportés à les données par une application sont immédiatement disponibles à une autre, y compris toute exécution dans un autre processus enfant, une donnée externe un stockage tel qu'une base de données ou une mémoire partagée doit être utilisé. Mondial les variables dans les modules Python normaux ne peuvent pas être utilisées à cette fin.
Mais dans ce cas, il devient vraiment lourd quand on veut être sûr qu'une l'application fonctionne dans toutes les conditions WSGI (y compris les multiprocesseurs).
par exemple, une variable simple qui contient le nombre actuel d'utilisateurs connectés - doit-elle être lisible/écrite à partir de/vers memcached, ou un DB ou (si de tels mécanismes de bibliothèque hors norme sont disponibles) une mémoire partagée?
Et le code est de la forme
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 comporter de manière imprévisible dans un environnement multiprocesseur?
Merci!
3 réponses
il y a plusieurs aspects à considérer dans votre question.
tout d'abord, l'interaction entre les applications MPM d'apache et mod_wsgi. Si vous exécutez l'application mod_wsgi en mode embarqué (Non WSGIDaemonProcess
nécessaire, WSGIProcessGroup %{GLOBAL}
) vous héritez de multiprocessing / multithreading des MPM apache. Cela devrait être l'option la plus rapide, et vous finissez par avoir plusieurs processus et plusieurs threads par processus, en fonction de votre configuration MPM. Au contraire, si vous exécutez dans mod_wsgi mode démon, avec WSGIDaemonProcess <name> [options]
et WSGIProcessGroup <name>
, vous avez un contrôle précis sur le multitraitement/multithreading au prix d'un petit surcharge.
dans un seul serveur apache2, vous pouvez définir zéro, un ou plusieurs nommés WSGIDaemonProcess
es, et chaque application peut être exécutée dans l'un de ces processus (WSGIProcessGroup <name>
) ou exécutez en mode embarqué avec WSGIProcessGroup %{GLOBAL}
.
vous pouvez vérifier multiprocessing / multithreading en inspectant le wsgi.multithread
et wsgi.multiprocess
variables.
Avec votre configuration WSGIDaemonProcess example processes=5 threads=1
vous avez 5 processus indépendants, chacun avec un seul fil d'exécution: pas de données globales, pas de mémoire partagée, puisque vous n'êtes pas en contrôle des sous-processus de fraie, mais mod_wsgi le fait pour vous. Pour partager un état global, vous avez déjà énuméré quelques options possibles: une DB à laquelle votre interface de processus, une sorte de persistance basée sur le système de fichiers, un processus démon (lancé en dehors d'apache) et une IPC basée sur socket.
comme L'a souligné Roland Smith, le ce dernier pourrait être implémenté en utilisant une API de haut niveau par multiprocessing.managers
: en dehors d'apache vous créez et démarrez un BaseManager
processus du serveur
m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.get_server().serve_forever()
et à l'intérieur de vous apps vous connect
:
m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.connect()
L'exemple ci-dessus est factice, car m
n'a aucune méthode utile enregistrée, mais ici (python docs) vous trouverez comment créer et proxy un objet (comme le counter
dans votre exemple) parmi vos processus.
un dernier commentaire sur votre exemple, avec processes=5 threads=1
. Je comprends que c'est juste un exemple, mais dans le monde réel je soupçonne que les performances seront comparables à l'égard de processes=1 threads=5
: vous ne devez entrer dans les complexités du partage de données dans le multiprocessing que si la performance attendue boost sur le modèle "single process many threads" est significative.
à Partir de la documentation sur les processus et les enfiler pour wsgi:
quand Apache est exécuté dans un mode où il y a plusieurs processus enfants, chaque processus enfant contiendra des sous-interprètes pour chaque application WSGI.
cela signifie que dans votre configuration, 5 processus avec 1 thread chacun, il y aura 5 interprètes et pas de données partagées. Votre contre-objet sera unique à chaque interprète. Vous devez construire une solution personnalisée sessions de comptage (un processus commun avec lequel vous pouvez communiquer, une sorte de solution basée sur la persistance, etc.) Ou, et c'est certainement ma recommandation, utiliser une solution prébuilée (Google Analytics et Chartbeat sont des options fantastiques).
j'ai tendance à penser à l'utilisation de globals pour partager des données comme une grande forme d'abus mondial. C'est un problème de bogue et de portabilité dans la plupart des environnements dans lesquels j'ai fait du traitement en parallèle. Si tout à coup votre application devait être exécuté sur plusieurs virtuel les machines? Cela briserait votre code quel que soit le modèle de partage des threads et des processus.
Si vous utilisez multiprocessing
, il y a plusieurs façons partager les données entre les processus. Valeurs et Tableaux ne fonctionnent que si les processus ont une relation parent/enfant (ils sont partagés par héritage). Si ce n'est pas le cas, utiliser un Manager
et Proxy
objets.