Tornado asynchrone Anfrage nicht blockierend

高洛峰
Freigeben: 2016-10-17 13:58:44
Original
1079 Leute haben es durchsucht

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()


Die meisten unserer Funktionen blockieren beispielsweise time.sleep oben in Tornado:


#!/bin /env python

tornado.httpserver importieren

tornado.ioloop importieren

tornado.options importieren

tornado.web importieren

import tornado.gen

tornado.httpclient importieren

tornado.concurrent importieren

tornado.ioloop importieren

Zeit importieren

aus Tornado. Optionen importieren define, Optionen

define("port", default=8000, help="run on the gegeben port", type=int)

class SleepHandler(tornado.web.RequestHandler) :

@tornado.web.asynchronous

@tornado.gen.coroutine

def get(self):

yield tornado.gen.Task( tornado.ioloop.IOLoop.instance ().add_timeout, time.time() 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()

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:


class SleepHandler(tornado.web.RequestHandler):

@tornado.web.asynchronous

def get(self):

tornado .ioloop.IOLoop.instance ().add_timeout(time.time() 5, callback=self.on_response)

def on_response(self):

self.write("when i sleep 5s")

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('

  • %s
  • ' % message['a'])

    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

  • ' % i [' a '])
  • 🎜>

    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-Celery

    Um das auszuführen Folgendes Programm, zuerst müssen Sie Rabbitmq und Sellerie installieren:


    #!/bin/env python

    import tornado.httpserver

    tornado.ioloop importieren

    tornado.options importieren

    tornado .web importieren

    tornado.gen importieren

    tornado.httpclient importieren

    Tcelery, Aufgaben importieren

    Zeit importieren

    aus tornado.options import define, Optionen

    define("port", default=8000, help="auf dem angegebenen Port ausführen ", type=int)

    tcelery.setup_nonblocking_producer()

    class SleepHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous

    @tornado.gen.coroutine

    def get(self):

    # 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()

    Verwandte Etiketten:
    Quelle:php.cn
    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
    Beliebte Tutorials
    Mehr>
    Neueste Downloads
    Mehr>
    Web-Effekte
    Quellcode der Website
    Website-Materialien
    Frontend-Vorlage