Asynchrone HTTP-Anforderungen in Python mit aiohttp und asyncio
Lesezeit: 4 Minuten
Asynchroner Code ist zunehmend zu einer tragenden Säule der Python-Entwicklung geworden. Mit asyncio als Teil der Standardbibliothek und vielen Paketen von Drittanbietern, die damit kompatible Funktionen bieten, wird dieses Paradigma nicht so schnell verschwinden.
Wir wollen uns anschauen, wie man die aiohttp-Bibliothek verwendet, um dies für asynchrone HTTP-Anforderungen zu nutzen. Dies ist einer der häufigsten Anwendungsfälle für nicht blockierenden Code.
Was ist nicht blockierender Code?
Möglicherweise hörst du Begriffe wie „asynchron“, „nicht blockierend“ oder „gleichzeitig“ und bist ein wenig verwirrt darüber, was sie alle bedeuten. Diesem viel detaillierteren Tutorial zufolge sind zwei der primären Eigenschaften:
- Asynchrone Routinen können pausieren, während sie auf ihr endgültiges Ergebnis warten, damit andere Routinen in der Zwischenzeit ausgeführt werden können.
- Asynchroner Code erleichtert durch den oben genannten Mechanismus die gleichzeitige Ausführung. Anders ausgedrückt: Asynchroner Code vermittelt das Erscheinungsbild von Parallelität.
Asynchroner Code ist also Code, der hängen bleiben kann, während auf ein Ergebnis gewartet wird, damit in der Zwischenzeit anderer Code ausgeführt werden kann. Er „blockiert“ keinen anderen Code, so dass wir ihn „nicht blockierenden“ Code nennen können.
Die asyncio-Bibliothek bietet Python-Entwicklern eine Vielzahl von Tools, um dies zu tun, und aiohttp bietet eine noch spezifischere Funktionalität für HTTP-Anforderungen. HTTP-Anforderungen sind ein klassisches Beispiel für etwas, das sich gut für Asynchronität eignet, da sie das Warten auf eine Antwort von einem Server beinhalten. Während dieser Zeit wäre es bequem und effizient, anderen Code auszuführen.
Einrichtung
Stelle sicher, dass deine Python-Umgebung eingerichtet ist, bevor du beginnst. Folge dieser Anleitung bis zum Abschnitt virtualenv, wenn du Hilfe benötigst. Es ist wichtig, dass alles richtig funktioniert, besonders in Bezug auf virtuelle Umgebungen, um deine Abhängigkeiten zu isolieren, wenn mehrere Projekte auf demselben Computer ausgeführt werden. Du benötigst mindestens Python 3.7 oder höher, um den Code in diesem Beitrag ausführen zu können.
Nachdem deine Umgebung eingerichtet ist, musst du einige Bibliotheken von Drittanbietern installieren. Wir werden aiohttp für asynchrone Anfragen und die Anfragen-Bibliothek zum Erstellen regelmäßiger synchroner HTTP-Anforderungen verwenden, um die beiden später zu vergleichen. Installiere beide mit dem folgenden Befehl, nachdem du deine virtuelle Umgebung aktiviert hast:
Damit solltest du bereit sein, fortzufahren und Code zu schreiben.
Stellen einer HTTP-Anfrage mit aiohttp
Beginnen wir mit einer einzelnen GET
-Anfrage mit aiohttp, um zu demonstrieren, wie die Schlüsselwörter async
und await
funktionieren. Wir werden das Pokemon API als Beispiel benutzen. Lasst uns zunächst versuchen, die Daten des legendären 151. Pokemon, Mew, zu erhalten.
Führe den folgenden Python-Code aus, und auf dem Terminal sollte der Name „mew“ angezeigt werden:
In diesem Code erstellen wir eine Coroutine namens main
, die wir mit dem asyncio event loop laufen lassen. Hier öffnen wir eine aiohttp client session, ein einzelnes Objekt, das für eine ganze Reihe von Einzelanforderungen verwendet werden kann und standardmäßig Verbindungen mit bis zu 100 verschiedenen Servern gleichzeitig herstellen kann. In dieser Sitzung stellen wir eine Anfrage an die Pokemon-API und warten dann auf eine Antwort.
Dieses Schlüsselwort async
teilt dem Python-Interpreter grundsätzlich mit, dass die von uns definierte Coroutine asynchron mit einer Ereignisschleife ausgeführt werden soll. Das Schlüsselwort await
gibt die Steuerung an die Ereignisschleife zurück, unterbricht die Ausführung der umgebenden Coroutine und lässt die Ereignisschleife andere Dinge ausführen, bis das Ergebnis „awaited“ zurückgegeben wird.
Stellen einer großen Anzahl von Anfragen.
Das Stellen einer einzelnen asynchronen HTTP-Anforderung ist großartig, da die Ereignisschleife bei anderen Aufgaben ausgeführt werden kann, anstatt den gesamten Thread zu blockieren, während auf eine Antwort gewartet wird. Diese Funktionalität ist jedoch wirklich hervorragend, wenn du versuchst, eine größere Anzahl von Anforderungen zu stellen. Lasst uns dies demonstrieren, indem wir dieselbe Anforderung wie zuvor ausführen, jedoch für alle 150 der ursprünglichen Pokémon.
Nehmen wir den vorherigen Anforderungscode und fügen ihn in eine Schleife ein. Dabei werden die angeforderten und verwendeten Pokemon-Daten aktualisiert und await
für jede Anfrage verwendet:
Dieses Mal messen wir auch, wie viel Zeit der gesamte Prozess benötigt. Wenn du diesen Code in deiner Python-Shell ausführst, sollte auf deinem Terminal Folgendes angezeigt werden:
8 Sekunden scheinen für 150 Anfragen ziemlich gut zu sein, aber wir haben nichts, mit dem wir es vergleichen könnten. Versuchen wir, dasselbe synchron mithilfe der Requests-Bibliothek zu erreichen.
Vergleichen der Geschwindigkeit mit synchronen Anforderungen
Requests wurde als HTTP-Bibliothek „für Menschen“ entwickelt und verfügt daher über eine sehr schöne und vereinfachte API. Ich kann es sehr für Projekte empfehlen, bei denen Geschwindigkeit im Vergleich zu Entwicklerfreundlichkeit und einfach zu befolgendem Code möglicherweise nicht von größter Bedeutung ist.
Führe den folgenden Code aus, um die ersten 150 Pokemon wie zuvor zu drucken, aber die Requests-Bibliothek zu verwenden:
Du solltest dieselbe Ausgabe mit einer anderen Laufzeit sehen:
Mit fast 29 Sekunden ist dies deutlich langsamer als der vorherige Code. Für jede aufeinanderfolgende Anfrage müssen wir warten, bis der vorherige Schritt abgeschlossen ist, bevor wir überhaupt mit dem Prozess beginnen. Es dauert viel länger, da dieser Code darauf wartet, dass 150 Anforderungen nacheinander abgeschlossen werden.
Verwendung von asyncio für eine verbesserte Leistung
8 Sekunden im Vergleich zu 29 Sekunden sind also ein enormer Leistungssprung, aber mit den Tools, die Asyncio
bietet, können wir noch bessere Ergebnisse erzielen. Im ursprünglichen Beispiel verwenden wir await
nach jeder einzelnen HTTP-Anfrage, was nicht ganz ideal ist. Es ist immer noch schneller als das Requests-Beispiel, da wir alles in Coroutinen ausführen. Stattdessen können wir alle diese Anforderungen „gleichzeitig“ als Asyncio-Tasks ausführen und dann die Ergebnisse am Ende mit asyncio.ensure_future
und asyncio.gather
überprüfen.
Wenn der Code, der die Anforderung tatsächlich stellt, in eine eigene Coroutine-Funktion aufgeteilt wird, können wir eine Liste von Aufgaben erstellen, die aus Futures für jede Anfrage besteht. Wir können diese Liste dann in einem gather-Aufruf entpacken, der sie alle zusammen ausführt. Wenn wir dann await
für asyncio.gather
verwenden, erhalten wir eine Iterable für alle übergebenen Futures zurück, wobei die Reihenfolge in der Liste beibehalten wird. Auf diese Weise warten wir nur einmal.
Führe den folgenden Code aus, um zu sehen, was passiert, wenn wir dies implementieren:
Dies reduziert unsere Zeit auf nur 1,53 Sekunden für 150 HTTP-Anfragen! Dies ist eine enorme Verbesserung gegenüber unserem ursprünglichen Beispiel für async/await. Dieses Beispiel ist vollständig nicht blockierend, sodass die Gesamtzeit für die Ausführung aller 150 Anforderungen in etwa der Zeit entspricht, die die längste Anforderung für die Ausführung benötigt hat. Die genauen Zahlen variieren je nach Internetverbindung.
Abschließende Gedanken
Wie du siehst, kann die Verwendung von Bibliotheken wie aiohttp zum Überdenken der Art und Weise, wie du HTTP-Anforderungen stellst, deinem Code eine enorme Leistungssteigerung verleihen und viel Zeit sparen, wenn du eine große Anzahl von Anforderungen stellst. Standardmäßig ist es etwas ausführlicher als synchrone Bibliotheken wie Requests, aber das ist beabsichtigt, da die Entwickler Leistung zu einer Priorität machen wollten.
In diesem Tutorial haben wir nur im Ansatz behandelt, was du mit aiohttp und asyncio tun kannst, aber ich hoffe, dass dies deine Reise in die Welt des asynchronen Python ein wenig erleichtert hat.
Ich bin gespannt auf eure Ergebnisse. Ihr könnt mich gerne kontaktieren, um eure Erfahrungen zu teilen oder Fragen zu stellen.
- E-Mail: sagnew@twilio.com
- Twitter: @Sagnewshreds
- Github: Sagnew
- Twitch (Live-Code-Streaming): Sagnewshreds
Verwandte Posts
Ähnliche Ressourcen
Twilio Docs
Von APIs über SDKs bis hin zu Beispiel-Apps
API-Referenzdokumentation, SDKs, Hilfsbibliotheken, Schnellstarts und Tutorials für Ihre Sprache und Plattform.
Ressourcen-Center
Die neuesten E-Books, Branchenberichte und Webinare
Lernen Sie von Customer-Engagement-Experten, um Ihre eigene Kommunikation zu verbessern.
Ahoy
Twilios Entwickler-Community-Hub
Best Practices, Codebeispiele und Inspiration zum Aufbau von Kommunikations- und digitalen Interaktionserlebnissen.