Einführung in ASGI: Aufbau eines asynchronen Python-Web-Ökosystems

Hallo habr Ich präsentiere Ihnen die Übersetzung des Artikels "Einführung in ASGI: Entstehung eines asynchronen Python-Web-Ökosystems" von Florimond Manca.



"Turtles Near The Pond", Ricard Baraham auf unsplash.com


Python ist nicht nur auf Data Science beschränkt, die Python-Webentwicklung ist mit einer neuen asynchronen Wendung in der Sprachentwicklung zurückgekehrt!


Viele wichtige Ereignisse finden im Python-Webentwicklungs-Ökosystem statt. Einer der Haupttreiber dieser Änderungen ist ASGI - Asynchronous Standard Gateway Interface.


Ich habe ASGI bereits mehrmals in meinem Blog erwähnt, insbesondere als ich Bocadillo ( asynchrones Open-Source-Python-Webframework - ungefähr per . ) Und Tartiflette-Starlette ( Bibliothek zum Erstellen von GraphQL-API über HTTP via ASGI - ungefähr per . ) Ankündigte. . ), aber ich habe nie eine ausführliche Einführung über ihn geschrieben. Jetzt werde ich es tun.


Dieser Artikel richtet sich an Personen, die an den neuesten Trends in der Python-Webentwicklung interessiert sind. Ich möchte Sie zu einem Ausflug einladen, bei dem Sie erfahren, was ASGI ist und was es für die moderne Webentwicklung in der Python-Welt bedeutet.


Bevor wir anfangen, möchte ich Ihnen sagen, dass ich kürzlich awesome-asgi erstellt habe - eine großartige Liste für die Verfolgung des ständig wachsenden ASGI-Ökosystems.


Alles begann mit async / await


Im Gegensatz zu JavaScript oder Go war Python zum Zeitpunkt seiner Veröffentlichung nicht in der Lage, Code asynchron auszuführen. Lange Zeit konnte die parallele Codeausführung in Python nur mithilfe von Multithread- oder Multiprozessor-Verarbeitung oder mithilfe spezialisierter Netzwerkbibliotheken wie eventlet, gevent oder Twisted realisiert werden. (Twisted verfügte bereits 2008 über eine API für asynchrone Coroutinen, beispielsweise in Form von inlineCallbacks und deferredGenerator )


In Python 3.4+ hat sich alles geändert. In Python 3.4 wurde asyncio in die Standardbibliothek aufgenommen, wodurch kooperatives Multitasking auf der Basis von Generatoren und Syntaxausbeute unterstützt wurde .


Später in Python 3.5 wurde die async/await Syntax hinzugefügt . Dank dessen erschienen native Coroutinen, unabhängig von der zugrunde liegenden Implementierung, was zu einem Goldrausch in Bezug auf die Parallelität in Python führte.


Das verrückte Rennen hat begonnen! Seit der Veröffentlichung von Version 3.5 hat die Community alles buchstäblich asynchronisiert. Wenn Sie interessiert sind, werden viele der resultierenden Projekte in aio-libs und awesome-asyncio aufgelistet .


Wie Sie sich denken können, bedeutet dies auch, dass Webserver und Python-Anwendungen in Richtung Asynchronität gehen. Tatsächlich machen es alle coolen Jungs! ( Sogar Django ) ( Habr: Django 3.0 wird asynchron sein , bereits am 12/02/2019 veröffentlicht - ungefähr pro. )


ASGI Rückblick


Wie passt ASGI in all das?


ASGI auf höchster Ebene kann als Verbindung betrachtet werden, über die asynchrone Python-Server und -Anwendungen miteinander interagieren können. Er wiederholt viele der Architekturideen von WSGI und wird oft als sein Nachfolger mit eingebauter Asynchronität dargestellt.


So kann es im Diagramm dargestellt werden:



Auf sehr hohem Niveau ist ASGI eine Schnittstelle für die Kommunikation zwischen Anwendungen und Servern. Tatsächlich ist alles etwas komplizierter.


Um zu verstehen, wie ASGI wirklich funktioniert, werfen wir einen Blick auf die ASGI-Spezifikation .


ASGI besteht aus zwei verschiedenen Komponenten:


  1. Protokollserver - überwacht Sockets und konvertiert sie in Verbindungen und Ereignismeldungen innerhalb jeder Verbindung.
  2. Eine Anwendung ( Application ), die sich innerhalb des Protokollservers befindet, deren Instanz einmal für jede Verbindung erstellt wird und Ereignismeldungen verarbeitet, sobald sie auftreten.

Entsprechend der Spezifikation gibt ASGI das Nachrichtenformat an und wie diese Nachrichten zwischen der Anwendung und dem Protokollserver, auf dem sie ausgeführt werden, übertragen werden sollen.


Jetzt können wir eine detailliertere Version des Diagramms erstellen:



Das Protokoll enthält viele weitere interessante Details. Sie können sich beispielsweise die HTTP- und WebSocket-Spezifikationen ansehen.


Obwohl sich die Spezifikation stark auf die Interaktion zwischen Server und Anwendung konzentriert, kann ASGI viele weitere Aspekte abdecken.


Wir kommen in einer Minute dazu, aber zuerst ...


ASGI-Grundlagen


Nachdem wir gesehen haben, wie ASGI in das Python-Web-Ökosystem passt, wollen wir uns genauer ansehen, wie dies in Code übersetzt wird.


ASGI basiert auf einem einfachen Modell: Wenn ein Client eine Verbindung zu einem Server herstellt, wird eine Anwendungsinstanz erstellt. Anschließend werden die eingehenden Daten an die Anwendung übertragen und alle von ihr zurückgegebenen Daten werden zurückgesendet.


Das Übergeben von Daten an die Anwendung bedeutet hier tatsächlich das Aufrufen der Anwendung, als ob es eine Funktion wäre, d. H. Etwas, das Eingaben benötigt und Ausgaben zurückgibt.


Tatsächlich ist alles, was eine ASGI-Anwendung darstellt, ein aufrufbares Objekt. Die Parameter dieses aufgerufenen Objekts werden wiederum durch die ASGI-Spezifikation bestimmt :


 async def app(scope, receive, send): ... 

Die Signatur dieser Funktion ist genau das, was "I" in "ASGI" bedeutet: Die Schnittstelle, die die Anwendung implementieren muss, damit der Server sie aufruft.


Schauen wir uns die Parameter der Funktion an:


  • scope ist ein Wörterbuch, das Informationen zu einer eingehenden Anfrage enthält. Sein Inhalt unterscheidet sich für HTTP- und WebSocket- Verbindungen.
  • receive ist eine asynchrone Funktion zum Empfangen von Nachrichten über ASGI-Ereignisse.
  • send ist eine asynchrone Funktion zum Senden von Nachrichten über ASGI-Ereignisse.

Tatsächlich können Sie mit diesen Parametern Daten über den vom Protokollserver unterstützten Kommunikationskanal empfangen ( receive() ) und senden ( send() sowie nachvollziehen, in welchem ​​Kontext (oder scope ) dieser Kanal erstellt wurde.


Ich weiß nichts über dich, aber ich mag das allgemeine Erscheinungsbild und die Struktur dieser Benutzeroberfläche. Schauen wir uns jetzt auf jeden Fall einen Beispielcode an.


Zeige den Code!


Um eine praktische Vorstellung davon zu bekommen, wie ASGI aussieht, habe ich ein Minimalprojekt für ASGI erstellt, das eine HTTP-Anwendung demonstriert, die von uvicorn (einem beliebten ASGI-Server) bereitgestellt wird:


 async def app(scope, receive, send): assert scope["type"] == "http" await send({ "type": "http.response.start", "status": 200, "headers": [ [b"content-type", b"text/plain"], ] }) await send({ "type": "http.response.body", "body": b"Hello, world!", }) 

Quellcode - https://glitch.com/edit/#!/asgi-hello-world


Hier verwenden wir send() , um eine HTTP-Antwort an den Client zu senden: Zuerst senden wir die Header und dann den Antworttext.


Ich gebe zu, dass nackte ASGI aufgrund all dieser Wörterbücher und binären Rohdaten für die Arbeit nicht sehr praktisch ist.


Glücklicherweise gibt es übergeordnete Optionen - und dann fange ich an, über Starlette zu sprechen.


Starlette ist ein wirklich fantastisches Projekt und meiner Meinung nach ein grundlegender Bestandteil des ASGI-Ökosystems.


Kurz gesagt, es enthält eine Reihe von Komponenten auf hoher Ebene, z. B. Anforderungen und Antworten, mit denen Sie einige Details von ASGI abstrahieren können. Schauen Sie sich hier die "Hallo Welt" in Starlette an:


 # app.py from starlette.responses import PlainTextResponse async def app(scope, receive, send): assert scope["type"] == "http" response = PlainTextResponse("Hello, world!") await response(scope, receive, send) 

Starlette bietet alles, was Sie von einem echten Webframework erwarten - Routing, Middleware usw. Aber ich habe mich entschlossen, diese abgespeckte Version zu zeigen, um auf die wahre Kraft von ASGI hinzuweisen, die ...


Schildkröten den ganzen Weg


Ein interessantes und regelveränderndes Konzept für ASGI ist Turtles All the Way , ein Ausdruck, der ursprünglich von Andrew Godwin erfunden wurde (glaube ich). Er hat Django Migrations erstellt und arbeitet derzeit an Django, um die Asynchronität zu unterstützen .


Aber was genau bedeutet das?


Da es sich bei ASGI um eine Abstraktion handelt, mit der wir jederzeit feststellen können, in welchem ​​Kontext wir uns befinden, und Daten empfangen und senden können, besteht die Idee, dass ASGI nicht nur zwischen Servern und Anwendungen, sondern auch wirklich überall im Stapel verwendet werden kann.


Das Starlette Response Objekt ist beispielsweise die ASGI-Anwendung selbst. Tatsächlich können wir den Code in der obigen Beispielanwendung folgendermaßen kürzen:


 # app.py app = PlainTextResponse("Hello, world!") 

Wie lächerlich sieht das aus ?!


Aber warte, das ist noch nicht alles.


Die tiefere Konsequenz von „Turtles all the way“ ist, dass wir alle Arten von Anwendungen, Middleware, Bibliotheken und anderen Projekten erstellen und sicherstellen können, dass sie kompatibel sind, solange sie alle die ASGI-Anwendungsschnittstelle implementieren.


(Aus meiner persönlichen Erfahrung beim Erstellen von Bocadillo führt das Akzeptieren der ASGI-Schnittstelle sehr oft (wenn nicht immer) zu viel saubererem Code.)


Beispielsweise können wir eine ASGI-Middleware (d. H. Eine Anwendung, die eine andere Anwendung umschließt) erstellen, um die Zeit anzuzeigen, die für die Fertigstellung der Anforderung benötigt wurde:


 # app.py import time class TimingMiddleware: def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): start_time = time.time() await self.app(scope, receive, send) end_time = time.time() print(f"Took {end_time - start_time:.2f} seconds") 

Um es zu benutzen, wickeln wir einfach die Anwendung damit ein ...


 # app.py import asyncio from starlette.responses import PlainTextResponse async def app(scope, receive, send): await asyncio.sleep(1) response = PlainTextResponse("Hello, world!") await response(scope, receive, send) app = TimingMiddleware(app) 

... und es wird magisch einfach funktionieren.


 $ uvicorn app:app INFO: Started server process [59405] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ... INFO: ('127.0.0.1', 62718) - "GET / HTTP/1.1" 200 Took 1.00 seconds 

Es ist TimingMiddleware dass Sie in TimingMiddleware jede ASGI-Anwendung einbinden können. Die interne Anwendung in diesem Beispiel ist sehr einfach, aber es kann sich um ein vollwertiges reales Projekt handeln (stellen Sie sich Hunderte von APIs und WebSocket-Endpunkten vor) - es spielt keine Rolle, solange die Schnittstelle mit ASGI kompatibel ist.


(Es gibt eine Version dieser Middleware, die eher für den industriellen Einsatz vorbereitet ist: timing-asgi .)


Warum sollte es stören?


Die Kompatibilität ist meines Erachtens ein sehr starkes Argument, aber die Verwendung von ASGI-basierten Komponenten zum Erstellen von Python-Webanwendungen bietet noch viele weitere Vorteile.


  • Geschwindigkeit: Die asynchrone Natur von ASGI-Anwendungen und -Servern macht sie sehr schnell (zumindest für Python) - es handelt sich um 60.000 bis 70.000 Anfragen pro Sekunde (vorausgesetzt, Flask und Django erreichen in einer ähnlichen Situation nur 10 bis 20.000).
  • Features: ASGI-Server und -Plattformen bieten Zugriff auf im Wesentlichen parallele Funktionen (WebSocket, Server-Sent Events, HTTP / 2), die nicht mit synchronem Code und WSGI implementiert werden können.
  • Stabilität: ASGI als Spezifikation gibt es seit 3 ​​Jahren und Version 3.0 gilt als sehr stabil. Infolgedessen stabilisieren sich die Hauptteile des Ökosystems.

Aus der Sicht der Bibliotheken und Tools kann ich nicht sagen, dass wir das erforderliche Niveau erreicht haben. Aber dank einer sehr aktiven Community habe ich große Hoffnungen, dass das ASGI-Ökosystem sehr bald die Funktionsgleichheit mit dem traditionellen synchronen / WSGI-Ökosystem erreicht.


Wo finde ich mit ASGI kompatible Komponenten?


Tatsächlich bauen und verbessern immer mehr Menschen Projekte, die auf ASGI basieren. Dies sind natürlich Server und Web-Frameworks, aber es gibt auch Middleware- und produktorientierte Anwendungen wie Datasette .


Nachfolgend einige Beispiele für nicht webbasierte Komponenten, die mich interessieren:



Es ist erstaunlich zu beobachten, dass sich das Ökosystem erfolgreich entwickelt, aber es war für mich persönlich schwierig, mit den Veränderungen Schritt zu halten.


Deshalb habe ich awesome-asgi erstellt . Ich hoffe, dies hilft allen, mit den erstaunlichen Dingen Schritt zu halten, die in der ASGI-Welt passieren. (Und da er in ein paar Tagen fast 100 Sterne erreicht hat, hatte ich das Gefühl, dass es wirklich notwendig war, Informationen über ASGI-Ressourcen an einem Ort zu sammeln.)


Schlussfolgerungen


Obwohl dies wie Implementierungsdetails aussehen mag, bin ich sicher, dass ASGI den Grundstein für eine neue Ära in der Python-Webentwicklung gelegt hat.


Wenn Sie mehr über ASGI erfahren möchten, lesen Sie die verschiedenen Veröffentlichungen (Artikel und Reden) in awesome-asgi . Wenn Sie es anfassen möchten, versuchen Sie eines der folgenden Projekte:



Diese Projekte wurden von Encode erstellt und werden hauptsächlich von Tom Christie unterstützt. Es gibt offene Diskussionen über die Einrichtung eines Encode-Supportteams. Wenn Sie also nach einer Möglichkeit suchen, an der Open-Source-Entwicklung teilzunehmen, haben Sie eine solche Gelegenheit!


Viel Spaß beim Reisen in die ASGI-Welt!

Source: https://habr.com/ru/post/de482936/


All Articles