2010-10-06 29 views
6

donc j'ai un gestionnaire ci-dessous:Python Tornado - POST faire revenir immédiatement en fonction async continue à travailler

class PublishHandler(BaseHandler): 

    def post(self): 
     message = self.get_argument("message") 
     some_function(message) 
     self.write("success") 

Le problème que je suis face est que une_fonction() prend un certain temps pour exécuter et je voudrais le post demande de retourner tout de suite lorsqu'il est appelé et que some_function() soit exécuté dans un autre thread/processus si possible. J'utilise berkeley db comme base de données et ce que j'essaie de faire est relativement simple.

J'ai une base de données d'utilisateurs avec un filtre. Si le filtre correspond au message, le serveur enverra le message à l'utilisateur. Actuellement, je teste avec des milliers d'utilisateurs et, par conséquent, à chaque publication d'un message via une demande de publication, iterating à travers des milliers d'utilisateurs pour trouver une correspondance. C'est ma façon naïve de faire les choses et d'où ma question. Comment est-ce que je fais mieux?

Répondre

7

Vous pourriez être en mesure d'accomplir ceci en utilisant votre méthode de add_callbackIOLoop comme ceci:

loop.add_callback(lambda: some_function(message)) 

Tornado exécutera passer le rappel dans la prochaine IOLoop, qui peut (je devrais creuser dans les tripes de Tornado pour savoir à coup sûr, ou alternativement le tester) permettent à la demande de se terminer avant que ce code soit exécuté. L'inconvénient est que le code à long terme que vous avez écrit prendra encore du temps à s'exécuter, ce qui peut bloquer une autre requête. Ce n'est pas idéal si vous avez beaucoup de ces demandes à la fois.

La solution la plus infaillible est de le faire fonctionner dans un fil ou un processus séparé. Le meilleur avec Python est d'utiliser un processus, en raison de la GIL (je recommande fortement de lire sur ce sujet si vous n'êtes pas familier avec lui). Cependant, sur une machine monoprocesseur, l'implémentation à thread fonctionnera tout aussi bien et peut être plus simple à implémenter.

Si vous allez sur la route filetée, vous pouvez créer un joli module "exécuteur asynchrone" avec un mutex, un thread et une file d'attente. Consultez le module multiprocessing si vous souhaitez utiliser un processus distinct.

1

J'ai essayé ceci, et je crois que la demande ne se termine pas avant que les rappels soient appelés.

Je pense que bidouille serait d'appeler deux niveaux de add_callback, .: par exemple

def get(self): 
    ... 
    def _defered(): 
     ioloop.add_callback(<whatever you want>) 
    ioloop.add_callback(_defered) 
    ... 

Mais ceux-ci sont hacks au mieux. Je suis à la recherche d'une meilleure solution en ce moment, probablement se retrouver avec une file d'attente de messages ou une solution de thread simple.