Sont des variables globales thread-safe dans le flacon? Comment puis-je partager des données entre les demandes?
Dans mon application de l'état d'un objet commun est changé en faisant des demandes, et la réponse dépend de l'état.
class SomeObj():
def __init__(self, param):
self.param = param
def query(self):
self.param += 1
return self.param
global_obj = SomeObj(0)
@app.route('/')
def home():
flash(global_obj.query())
render_template('index.html')
Si je lance sur mon serveur de développement, je m'attends à obtenir 1, 2, 3 et ainsi de suite. Si les demandes sont fabriqués à partir de 100 différents clients simultanément, quelque chose peut aller mal? Le résultat attendu serait que le 100 clients différents, chacun d'un numéro unique de 1 à 100. Ou sera quelque chose comme cela arrivent:
- Client 1 requêtes.
self.param
est incrémenté de 1. - Avant l'instruction return peut être exécutée, les commutateurs de thread sur le client 2.
self.param
est incrémenté de nouveau. - Le thread permet au client 1, et le client est renvoyé le numéro 2, dire.
- Maintenant le fil se déplace vers le client 2 et renvoie le numéro 3.
Car il n'y avait que deux clients, les résultats attendus sont 1 et 2, pas 2 et 3. Un certain nombre a été ignorée.
Va de ce fait se passer comme je l'échelle de ma demande? Quelles sont les alternatives à une variable globale dois-je regarder?
Vous devez vous connecter pour publier un commentaire.
Vous ne pouvez pas utiliser des variables globales pour la tenue de ce genre de données. Non seulement il n'est pas thread-safe, il n'est pas processus sûr, et WSGI serveurs en production engendrer de multiples processus. Non seulement votre compte tort si vous étiez à l'aide de threads pour gérer les demandes, ils varient également en fonction du processus qui a traité la demande.
Utiliser une source de données à l'extérieur du Flacon pour contenir des données globales. Une base de données, memcached, ou redis sont tous appropriés séparer les zones de stockage, en fonction de vos besoins. Si vous avez besoin de charger et d'accéder Python données, examiner
multiprocessing.Manager
. Vous pouvez également utiliser la session pour de simples données par utilisateur.Le serveur de développement peut s'exécuter dans le thread unique et processus. Vous ne verrez pas le comportement que vous décrivez depuis chaque demande sera traitée de manière synchrone. Activer threads ou processus et vous le verrez.
app.run(threaded=True)
ouapp.run(processes=10)
. (1.0 le serveur est enfilé par défaut.)Certains WSGI serveurs peuvent soutenir gevent ou d'une autre async travailleur. Les variables globales sont toujours pas thread-safe, car il n'y a toujours pas de protection contre la plupart des conditions de course. Vous pouvez toujours avoir un scénario où un travailleur devient une valeur, les rendements, l'autre modifie, les rendements, alors que le premier travailleur modifie.
Si vous avez besoin de stocker des données globales pendant une demande, vous pouvez utiliser le Flacon de
g
de l'objet. Un autre cas courant est certain objet de plus haut niveau qui gère les connexions de base de données. La distinction de ce type de "global" est qu'il est unique pour chaque demande, n'a pas utilisé entre demandes, et il y a quelque chose de gestion de la configuration et de la destruction de la ressource.Ce n'est pas vraiment une réponse à la sécurité des threads de variables globales.
Mais je pense qu'il est important de mentionner les séances ici.
Vous êtes à la recherche d'un moyen pour stocker client-spécifiques de données. Chaque connexion doit avoir accès à son propre pool de données, dans un des threads façon.
C'est possible avec le serveur-côté sessions, et ils sont disponibles dans une très soigné flacon plugin: https://pythonhosted.org/Flask-Session/
Si vous mettez en place des sessions, une
session
variable est disponible dans toutes tes voies, et il se comporte comme un dictionnaire. Les données stockées dans ce dictionnaire est individuelle pour chaque client connecté.Voici une petite démo:
Après
pip install Flask-Session
, vous devriez être en mesure de l'exécuter. Essayez d'y accéder à partir de différents navigateurs, vous verrez que le compteur n'est pas partagé entre eux.