2015-09-11 1 views
3

J'ai une application python (basée sur un modèle MVC) servie par un serveur Gunicorn utilisant une classe de travail asynchrone (c'est-à-dire gevent). Cela signifie que plusieurs demandes de clients sont traitées simultanément par un processus de travail. Chaque requête http contient des données spécifiques à cette requête, telles que 'user_id'. Dites une erreur s'est produite dans un modèle et je veux enregistrer l'erreur avec le user_id. Je ne veux pas continuer à passer l'id_utilisateur (et d'autres valeurs spécifiques à la requête) à chaque classe ou méthode. Je veux que ces valeurs soient disponibles globalement pour tout code exécuté pour cette requête particulière. Le contrôleur à la réception de la requête définit ces valeurs et tout code exécuté pour cette requête a accès à ces valeurs. Le code s'exécutant pour plusieurs demandes simultanées devrait avoir accès à leurs valeurs de données respectives. C'est possible?Gunicorn avec gevent: maintien par requête de données globales

+0

Bien sûr, jetez un oeil à Flask et comment il a gère ces globals. Vous pouvez essentiellement importer des modules comme d'habitude en Python et avoir accès à l'objet de requête actuel et à d'autres choses que vous pourriez vouloir placer dans la portée globale. Voici une question pertinente avec un aperçu intéressant: http://stackoverflow.com/questions/15083967/when-should-flask-g-be-used – Alp

Répondre

2

L'idée générale est d'associer vos données par requête à quelque chose qui peut être unique pour chaque requête. Par exemple, ayez une dict avec cet identifiant unique en tant que clés et les données per-request en tant que valeur.

Puisque vous dites que vous utilisez des travailleurs de gevent, nous pouvons utiliser greenlet.getcurrent() comme identifiant unique. C'est ce que font Flask + Werkzeug, mais ils le font de manière beaucoup plus performante, efficace en termes de mémoire, compatibles avec les threads et conviviaux pour l'utilisateur final que mon exemple ci-dessous.

Voici une application wsgi simple à servir d'exemple. Ici a est défini et source sur un dict par requête provenant de la fonction 'globalement disponible' get_per_greenlet_dict. Considérant que b est passé en tant que paramètre pour servir de vérification que a est correcte.

# wsgi.py 
import collections, logging, time, greenlet 

logging.basicConfig() 
log = logging.getLogger(__name__) 
log.level = logging.DEBUG 

# used to store per-request data 
# keys are greenlets, values are dicts 
storage = collections.defaultdict(dict) 

# return a dict for this request 
# TODO: remove the per-request dict at the end of the request 
def get_per_greenlet_dict(): 
    return storage[greenlet.getcurrent()] 

def application(env, start_response): 

    # extract query vars 
    query_vars = env['QUERY_STRING'].split("&") 
    a = query_vars[0].split("=")[1] 
    b = query_vars[1].split("=")[1] 

    # store 'a' in our per-request dict 
    get_per_greenlet_dict()['a'] = a 

    log_a_and_b("Before sleep", b) 
    time.sleep(1) 
    log_a_and_b("After sleep", b) 

    start_response('200 OK', [('Content-Type', 'text/html')]) 
    return [b"OK: "] 


def log_a_and_b(prefix, b): 
    # log both a and b, 
    # where a is sourced from our per-request dict 
    # and b is passed as a parameter as a means of verifying a 
    a = get_per_greenlet_dict()['a'] 
    log.debug(prefix + "; a:%s b:%s", a, b) 

Exécutez le serveur gunicorn avec les travailleurs gevent:

$ gunicorn -k gevent wsgi 

exécuter plusieurs requêtes simultanée, disent par:

$ for i in `seq 1 5`; do curl "127.0.0.1:8000?a=$i&b=$i" & done 

Ensuite, vous verrez la sortie de gunicorn comme:

DEBUG:wsgi:Before sleep; a:2 b:2 
DEBUG:wsgi:Before sleep; a:5 b:5 
DEBUG:wsgi:Before sleep; a:4 b:4 
DEBUG:wsgi:Before sleep; a:1 b:1 
DEBUG:wsgi:Before sleep; a:3 b:3 
DEBUG:wsgi:After sleep; a:2 b:2 
DEBUG:wsgi:After sleep; a:5 b:5 
DEBUG:wsgi:After sleep; a:4 b:4 
DEBUG:wsgi:After sleep; a:1 b:1 
DEBUG:wsgi:After sleep; a:3 b:3 
+0

Merci. J'essaye werkzeug (http://werkzeug.pocoo.org/docs/0.10/local/) comme montré ici: http://stackoverflow.com/questions/14934885/threads-and-local-proxy-in-werkzeug- utilisation # answer-14935819 – Varun