Vorwort
Einige Studenten sind vielleicht verwirrt: Behauptet Tornado nicht, asynchron und nicht blockierend zu sein, um das 10K-Problem zu lösen, aber ich habe festgestellt, dass es nicht so ist, dass Torando schlecht ist, sondern dass Sie es sind Ich habe zum Beispiel kürzlich etwas entdeckt: Eine bestimmte Website war sehr langsam beim Öffnen der Seite und die CPU/der Speicher des Servers war normal. Später wurde festgestellt, dass beim Öffnen der Seite alles in Ordnung war. Es gab viele Anfragen für den Zugriff auf die Back-End-Datenbank, und es gab einen Restdienst der Mongodb-Datenbank-Geschäfts-API, aber der Tornado war nicht vorhanden. Wenn Sie ihn falsch verwendet haben, studieren Sie das Problem Schritt für Schritt:
Erklärung
Die folgenden Beispiele haben zwei URLs, eine ist eine zeitaufwändige Anfrage und die andere ist eine Anfrage, die meiner Meinung nach sofort zurückgegeben werden kann oder muss Selbst ein Benutzer, der sich mit der Technologie nicht auskennt, hofft logischerweise, dass seine Zugriffsanfrage die Anfragen anderer Personen nicht beeinflusst oder von diesen beeinflusst wird
#!/bin /env python
tornado.httpserver importieren
tornado.ioloop importieren
tornado.options importieren
tornado.web importieren
import tornado.httpclient
Importzeit
von tornado.options import define, options
define("port ", default=8000, help="run on the gegeben port" , type=int)
class SleepHandler(tornado.web.RequestHandler):
def get(self):
time.sleep(5)
self.write("when i sleep 5s")
class JustNowHandler(tornado.web.RequestHandler):
def get(self):
self.write( „Ich hoffe, wir sehen uns gerade“)
if __name__ == „__main__“:
tornado.options.parse_command_line( )
app = tornado.web.Application( handlers=[
(r"/sleep", SleepHandler), (r"/justnow", JustNowHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
Wenn Sie Seitenanfragen verwenden oder zuerst httpie, Curl und andere Tools verwenden Greifen Sie auf http://localhost:8000/sleep zu und dann auf http://localhost:8000/justnow. Sie werden feststellen, dass die Anfrage für /jsutnow sofort hätte zurückgegeben werden können. Es wird blockiert, bis die /sleep-Anfrage vorliegt
Warum wird meine Anfrage durch die /sleep-Anfrage blockiert? Wenn unsere Webanfragen normalerweise schnell genug sind, bemerken wir dieses Problem möglicherweise nicht, aber tatsächlich gibt es oft einige zeitaufwändige Prozesse, was bedeutet, dass die Anwendung effektiv gesperrt ist, bis die Verarbeitung abgeschlossen ist
Haben Sie sich zu diesem Zeitpunkt an den @tornado.web.asynchronous Decorator erinnert? Voraussetzung für die Verwendung dieses Dekorators ist jedoch, dass Sie für eine zeitaufwändige Ausführung eine asynchrone Ausführung durchführen müssen, wie z. B. time.sleep oben. Das bloße Hinzufügen des Dekorators hat keine Auswirkung, und es ist zu beachten, dass Tornado den Client standardmäßig schließt Die Funktion gibt „Ende der Verbindung“ zurück, aber wenn Sie den @tornado.web.asynchonous-Dekorator verwenden, schließt Tornado die Verbindung nie selbst und muss explizit von self.finish()
tornado.options. parse_command_line() app = tornado.web.Application(handlers=[ ) (r"/sleep", SleepHandler), (r"/justnow" , JustNowHandler)])
http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance() .start()Hier gibt es einen neuen Dekorator tornado.gen.coroutine nach 3.0. Die vorherige Methode bestand darin, Rückrufe zu verwenden, oder schauen Sie sich mein Beispiel an:
self.finish()
Callback wird verwendet, aber der neue Dekorator ermöglicht es uns, den gleichen Effekt durch yield zu erzielen: Wenn Sie /sleep öffnen und dann auf /justnow klicken, wird die Justnow-Anfrage sofort zurückgegeben, ohne dass dies beeinträchtigt wird. Der asynchrone Dekorator wird jedoch verwendet Ihre Zeit -verbrauchende Funktionen müssen auch asynchron ausgeführt werden
Die gerade genannten Beispiele sind bedeutungslos. Hier ist ein nützliches: Lesen Sie die Mongodb-Datenbankdaten und schreiben Sie sie dann aus Zeile für Zeile
#!/bin/env python
import tornado.htserver
import tornado.ioloop
tornado.options importieren
tornado.web importieren
tornado.gen importieren
tornado.httpclient importieren
tornado.concurrent importieren
import tornado.ioloop
Importzeit
# Ein von Mongodb erstellter Python-Treiber, der die asynchrone Datenbank
Importmotor
aus Tornado-Optionen unterstützt , Optionen
define("port", default=8000, help="run on the gegeben port", type=int)
# db ist eigentlich der Cursor der Testdatenbank
db = motor.MotorClient().open_sync().test
class SleepHandler(BaseHandler):
@tornado.web.asynchronous
@tornado . gen.coroutine
def get(self):
# Das Blockieren der Ausführung dieser Zeile dauert immer noch. Meine TT-Sammlung enthält einige Daten und keinen Index
Cursor = db.tt.find().sort([('a', -1)])
# Dieser Teil wird asynchron und nicht blockierend ausgeführt, ohne Auswirkungen auf andere Seitenanfragen
Yield Cursor.fetch_next):
message = Cursor.next_object()
self.write('
self.write('')
self.finish()
def _on_response(self, message, error):
if error:
raise tornado.web.httperror (500, Fehler)Elif Message:
FOR I in Message:
Self.write ('
> % s
Klasse JustNowHandler (Basehandler):
def det (self):
Selbst .write("Ich hoffe, wir sehen uns gerade")if __name__ == "__main__": tornado.options.parse_command_line() app = tornado.web .Application(handlers=[ ) (r"/sleep", SleepHandler ), (r"/justnow", JustNowHandler)]) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado .ioloop.IOLoop.instance().start()Ein Kollege fragte, warum das zeitaufwändige Ding nicht sein kann asynchron zur Ausführung an ein Tool geworfen, ohne meine Anfrage zu blockieren? Nun, ich habe auch darüber nachgedacht. Ja: Celery, nur Github hat dieses Ding: Tornado-CeleryUm das auszuführen Folgendes Programm, zuerst müssen Sie Rabbitmq und Sellerie installieren:
#!/bin/env python
import tornado.httpserver
tornado.ioloop importieren
# Die Parameter von tornado.gen.Task sind: die auszuführende Funktion, Parameter yield tornado.gen.Task(tasks.sleep.apply_async, args=[5]) self.write("when i sleep 5s") self.finish()class JustNowHandler(tornado.web.RequestHandler): def get(self): self.write("ich hoffe, wir sehen uns gerade")if __name__ == "__main__": tornado.options.parse_command_line() app = tornado.web.Application(handlers=[ (r"/sleep", SleepHandler), (r"/justnow", JustNowHandler)]) http_server = tornado.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()task ist eine Aufgabe von Sellerie. Die definierte Datei enthält die time.sleep-Funktion, über die wir sprechen
Importzeit
aus Sellerieimport Sellerie
celery = Celery("tasks", Broker="amqp://guest:guest@localhost:5672")
celery.conf.CELERY_RESULT_BACKEND = "amqp"
@celery .task
def sleep(seconds):
time.sleep(float(seconds))
return seconds
if __name__ == "__main__" :
celery.start()
Dann starten Sie den Sellerie-Worker (wie sonst führen Sie Ihre Aufgabe aus? Es braucht definitiv einen Verbraucher, der sie wegnimmt):
celery -A task worker --loglevel=info
Aber das Problem hier kann auch schwerwiegend sein: Unsere asynchrone Nichtblockierung hängt von Sellerie oder der Länge der Warteschlange, falls vorhanden, ab Wenn es viele Aufgaben gibt, muss es einfach warten, was sehr ineffizient ist. Gibt es eine Möglichkeit, meine synchrone Blockierungsfunktion auf asynchron umzustellen (oder vom Tornado-Dekorator verstanden und erkannt zu werden)? >
#!/bin/env python
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado. web
import tornado.httpclient
import tornado.gen
from tornado.concurrent import run_on_executor
# Diese Parallelitätsbibliothek wird mit Python3 geliefert. sudo pip muss installiert werden. installiere Futures
von concurrent.futures import ThreadPoolExecutor
import time
von tornado.options import define, options
define ("port", default= 8000, help="run on the gegeben port", type=int)
class SleepHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(2 )
#executor ist eine lokale Variable, nicht global
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self ):
# Wenn die von Ihnen durchgeführte asynchrone Ausführung einen Wert zurückgibt und weiterhin aufgerufen wird, können Sie dies tun (nur zur Demonstration), andernfalls geben Sie einfach direkt nach
res = yield self. sleep()
self.write("when i sleep %s s" % res)
self.finish()
@run_on_executor
def Sleep (self):
time.sleep(5 )
return 5
class JustNowHandler(tornado.web.RequestHandler):
def get(self ):
self.write("ich hoffe, wir sehen uns gerade")
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[
(r"/sleep", SleepHandler), (r"/justnow", JustNowHandler)]) .listen(options.port)
tornado.ioloop.IOLoop.instance().start()