2012-11-08 6 views
5

Ich mache eine Web-Anwendung mit Python + Tornado, die im Grunde Dateien an Benutzer dient. Ich habe keine Datenbank.Tornado Async Aufruf an eine Funktion

Die Dateien werden entweder direkt abgeholt und bedient, wenn sie verfügbar sind, oder spontan generiert, wenn nicht.

Ich möchte, dass die Clients in einer asynchronen Weise bedient werden, weil einige Dateien möglicherweise bereits verfügbar sind, während andere generiert werden müssen (also müssen sie warten, und ich möchte nicht, dass sie andere Benutzer blockieren).

Ich habe eine Klasse, die die Kommissionierung oder Generierung von Dateien verwaltet, und ich muss nur von Tornado anrufen.

Was ist der beste Weg (am effizientesten auf CPU und RAM), um das zu erreichen? Soll ich einen Thread verwenden? Ein Unterprozess? Eine einfache gen.Task like this one?

Außerdem möchte ich, dass meine Implementierung in Google App Engines funktioniert (ich denke, sie erlauben nicht, dass Unterprozesse erzeugt werden?).

Ich bin relativ neu in der async Web-Service, so dass jede Hilfe willkommen ist. Das genTask Beispiel ist in der Tat der beste Weg, einen Asynchron-Aufruf zu implementieren, und es ist aufgrund der Tatsache, dass das Beispiel eines Python Koroutine zu verwenden ist, die ich didn‘:

+1

Die gen.Task wird gut funktionieren. Wenn Ihr Code zum Abrufen/Erzeugen von Dateien nicht gut wiedergegeben werden kann, funktioniert ein Thread einwandfrei. Sie können auch mehrere Prozesse generieren, aber ich kenne die Einschränkungen für Google App Engine nicht. – sean

+0

Danke für die Antwort. Weißt du auch wie es klappen wird wenn ich mehrere gen.Task spawne? Ich meine, meine ** Instanzmethode ** kann ziemlich CPU- und zeitaufwendig sein, wenn diese Methode mehrmals von mehreren Benutzern aufgerufen wird, was wird passieren?Laufen alle Methoden parallel async oder warten die nächsten Methoden auf die vorherigen? (Beachten Sie, dass ich eine einzelne Instanz meiner Klasse verwende, die ich während meiner gesamten Anwendung verwende) – gaborous

+1

Völlig ausgeblendet, sollte es in Ordnung sein, aber unabhängig von Ihrer Lösung würde es ein Problem mit vielen Threads unabhängig davon werden. Wenn dies ein Problem ist, sollten Sie versuchen, in separate Prozesse aufzuteilen oder mehrere Instanzen hinter einer Lastverteilung auszuführen. – sean

Antwort

10

Ich habe die Antworten auf meine Fragen gefunden t verstehe auf den ersten Blick, weil ich dachte Ausbeute wurde nur verwendet, um einen Wert für Generatoren zurückgeben.

Konkretes Beispiel:

class MyHandler(tornado.web.RequestHandler): 

    @asynchronous 
    @gen.engine 
    def get(self): 
     response = yield gen.Task(self.dosomething, 'argument') 

Was wichtig ist, ist hier die Kombination von zwei Dingen:

  • Ausbeute, die in der Tat eine Koroutine laicht (oder pseudo-Thread, der ist sehr effizient und sind sehr parallel-freundlich). http://www.python.org/dev/peps/pep-0342/

  • gen.Task(), die eine nicht-blockierende (async) Funktion, denn wenn man eine Koroutine auf einer Sperrfunktion laichen, wird es nicht async sein. gen.Task() wird von Tornado zur Verfügung gestellt, speziell um mit der Coroutinsyntax von Python zu arbeiten. Mehr Infos: http://www.tornadoweb.org/documentation/gen.html

So ein kanonisches Beispiel eines Asynchron-Aufruf in Python mit Koroutinen:

response = yield non_blocking_func(**kwargs) 
+1

Auch hier ist ein weiterer Beitrag, wo sowohl die Coroutine als auch die alte Callback-Implementierung ausführlich beschrieben sind: http://StackOverflow.com/Questions/8812715/using-a-Simple-Python-generator-as-A-Co-Routine-in -a-tornado-async-handler? rq = 1 – gaborous

1

Jetzt Documentation haben Lösung.

Einfaches Beispiel:

import os.path 
import tornado.web 
from tornado import gen 

class MyHandler(tornado.web.RequestHandler): 

    @gen.coroutine 
    def get(self, filename): 
     result = yield self.some_usefull_process(filename) 
     self.write(result) 

    @gen.coroutine 
    def some_usefull_process(self, filename): 
     if not os.path.exists(filename): 
      status = yield self.generate_file(filename) 
      result = 'File created' 
     else: 
      result = 'File exists' 

     raise gen.Return(result) 

    @gen.coroutine 
    def generate_file(self, filename): 
     fd = open(filename, 'w') 
     fd.write('created') 
     fd.close() 
Verwandte Themen