Watch a demoFree trial
Blog

Python Gevent in der Praxis: Häufige Fallstricke, die es zu beachten gilt

PythonPerformance
Teilen Sie
Dieser Beitrag ist auch auf Englische.

Gevent ist eine Python-Netzwerkbibliothek, die libev und libuv für ihre Event Loops und greenlet für asynchrone Aufgaben verwendet und damit wesentliche Abstraktionen für die Serverentwicklung bietet.

Historisch gesehen, nutzte Eve Online Stackless Python für seine Backend-Server, unter Verwendung von Tasklets oder Microthreads. Diese Tasklets erlaubten es, Tausende von Anfragen parallel in einem einzigen Thread auszuführen und so die mit Threads verbundenen Leistungs- und Komplexitätsprobleme zu vermeiden. Stackless Python entwickelte sich später zu Eventlet und inspirierte Gevent, eine asynchrone Bibliothek, die sich ideal für die Entwicklung von Hochleistungs-Netzwerkanwendungen eignet und von der Pythons Lesbarkeit und kurzen Entwicklungszeit profitiert. Diese Faktoren haben wesentlich dazu beigetragen, dass Upsun für einige interne Dienste wie unsere Projekt-API Gevent gewählt hat.

Dieser Artikel geht davon aus, dass der Leser mit den Kernkonzepten von Python Gevent wie kooperatives Multitasking, Event Loops, Greenlets und concurrent Scheduling vertraut sind. Sie können einige gute Artikel zu diesen Themen hier und hier finden. Dies erlaubt uns, uns in diesem Blog auf bewährte Praktiken und häufige Fallstricke in Gevent zu konzentrieren, die oft auch für andere asynchrone Bibliotheken gelten.

Häufige Fallstricke

Monkey-Patching

In Gevent ist Monkey-Patching eine optionale Technik, die die blockierenden Aufrufe der Standardbibliothek durch kooperative Alternativen ersetzt und in der Praxis weit verbreitet ist. Bei der Entscheidung, Monkey-Patching nicht zu verwenden, sind Sie selbst für die Greenlets verantwortlich, was je nach Anwendungsfall wünschenswert sein kann.

Gevent wurde zu einer Zeit entwickelt, als Python noch keine asynchrone Programmierung unterstützte - es gab keine async- oder await-Schlüsselwörter. Traditionelle Webserver-Anwendung wurden entweder mit einem Pre-Fork-Modell erstellt oder waren auf mehrere Threads für die Concurrency angewiesen. Das Ziel von Gevent war es, vorhandenen Multithreading-Code concurrent zu machen, ohne auf Threads des Betriebssystems angewiesen zu sein. In manchen Fällen konnte dies sogar ohne Änderung einer einzigen Codezeile in Ihrer Anwendung erreicht werden.

Wenn beispielsweise in einer Multithreading-Umgebung eine blockierende Funktion wie `socket.recv` aufgerufen wird, kümmert sich der Kernel um das Scheduling der Threads. Im Gegensatz dazu verwendet Gevent leichtgewichtige Threads, so genannte Greenlets, die gleichzeitig innerhalb eines einzigen Betriebssystem-Threads laufen. Um mehrere blockierende Funktionen innerhalb dieses einen Threads zu handhaben, monkey-patcht Gevent die Python-Standardbibliothek. Dabei werden die blockierenden Funktionen der Bibliothek durch Versionen ersetzt, die die Event Loop verwenden, um asynchron auf den Abschluss von Operationen zu warten.

Damit das Monkey-Patching effektiv funktioniert, müssen bestimmte Richtlinien eingehalten werden:

  • Das Patching sollte so früh wie möglich im Code durchgeführt werden. Bei späteren Einsatz, besteht die Gefahr, dass ein anderer Teil Ihres Codes ein Modul importiert, das die ursprünglichen, nicht gepatchten, Funktionen verwendet, was zu Konflikten führt.
  • Monkey-Patching sollte auf dem Hauptthread durchgeführt werden. Dadurch wird sichergestellt, dass die Patches in allen Greenlets wirksam werden.
  • Der Patching-Prozess sollte erfolgen, wenn die Anwendung noch Single-Thread-fähig ist, da das Monkey-Patching von Gevent auch die Threading-Bibliothek verändert. Wenn bereits mehrere Threads mit dem ungepatchten Threading-Modul erzeugt haben, könnten Konflikte entstehen.

Bei diesen Richtlinien handelt es sich nicht nur um Best Practices, sondern sie stammen direkt aus der offiziellen Dokumentation für das Monkey-Patching von Gevent. Die Nichtbeachtung dieser Regeln kann zu unvorhersehbaren, schwer zu diagnostizierenden Fehlern aufgrund von Konflikten zwischen gepatchtem und ungepatchtem Code führen.

Blockierende Aufrufe

Selbst wenn das Monkey-Patching in Gevent sorgfältig implementiert haben, kann ein einziges Greenlet den gesamten Anwendungsprozess blockieren. Dies gilt insbesondere für Lese- und Schreibvorgänge von Dateien. Trotz der Vorteile von Monkey Patching sind I/O-Operationen auf einigen Betriebssystemen immer noch synchron, was bedeutet, dass sie zu einem Engpass werden können.

Es gibt jedoch eine Strategie, um diese Einschränkung zu umgehen. Python Gevent bietet die Möglichkeit, konkurrente I/O-Operationen unter Verwendung eines Thread-Pools durchzuführen. Dieser Ansatz ermöglicht es, die Dateioperationen so zu behandeln, dass der Rest der Anwendung nicht blockiert wird. Während Monkey-Patching also viele Probleme im Zusammenhang mit asynchroner Programmierung löst, ist es wichtig, sich der Limitationen bewusst zu sein und zu wissen mit ihnen umzugehen um eine wirklich non-blocking Anwendung zu entwickeln.

Drittanbiert Bibliotheken

Trotz der Fähigkeiten von Gevent ist es wichtig zu wissen, dass es Bibliotheken von Drittanbietern nicht asynchron machen kann, wenn diese nicht die Standardbibliothek von Python für blockierende Aufrufe verwenden. Wenn eine externe Bibliothek verwendet wird, die ihre eigenen blockierenden Aufrufe macht, wird das Problem möglicherweise erst, wenn sich Ihre Anwendung in einer Produktionsumgebung befindet, entdeckt.

An dieser Stelle können Performanceregressionstests oder Belastungstests hilfreich sein. Wenn ein Greenlet bei der I/O blockiert ist, äußert sich das Problem in der Regel durch eine suboptimale CPU-Auslastung bei stagnierender Datenrate. Unter normalen Umständen sollten man nur dann an Grenzen stoßen, wenn die CPU-Auslastung bei oder über 100 % liegt. Wenn also beim Load Testing eine niedrige CPU-Auslastung und kein Anstieg der Datenrate festgestellt wird, ist dies wahrscheinlich ein Zeichen für ein blockierendes Greenlet.

Um dies zu beheben, muss die problematische Bibliothek eines Drittanbieters und ihre spezifische blockierende Funktion identifiziert werden. Die Lösung kann darin bestehen, zu einer alternativen Bibliothek zu wechseln oder die aktuelle Bibliothek zu modifizieren, obwohl beides in der Regel nicht einfach ist.

CPU-intensiver Code

Die Ausführung von CPU-intensivem Code in einem einzelnen Python-Greenlet kann die Ausführung anderer Greenlets blockieren, was zu einer so genannten Greenlet-Starvation führt. Die Fehlersuche in diesem Fall ist komplex, da man die Trends beim Kontextwechsel beobachten muss, um festzustellen, ob bestimmte Greenlets die CPU-Zeit für sich beanspruchen.

Um dieses Problem zu lösen, gibt es mehrere Möglichkeiten:

  1. Verteilung CPU-lastige Aufgaben auf einen separaten Prozess oder Thread, idealerweise unter Verwendung eines Pools. Auf diese Weise werden I/O-gebundene und CPU-gebundene Aufgaben voneinander getrennt, so dass sie sich nicht gegenseitig behindern können.
  2. Wenn eine Trennung der Arbeitslasten nicht möglich ist, weil z. B. eine CPU-intensive Aufgabe ausgeführt werden soll und das Ergebnis sofort zurückgegeben werden müssen, wird das Greenlet explizit geyieldet, damit die Ausführung anderer Greenlets möglich wird.

Es ist wichtig zu beachten, dass Gevent nicht ideal für CPU-intensive Aufgaben ist. Diese Einschränkung gilt nicht nur für Gevent; auch andere asynchrone Bibliotheken wie Asyncio und Node.js stehen vor ähnlichen Herausforderungen.

Greenlet-Sicherheit

Thread-Sicherheit ist ein bekanntes Konzept in der Multithreading-Welt. Es beinhaltet den Schutz gemeinsam genutzter Ressourcen vor dem gleichzeitigen Zugriff durch mehrere Threads. Während herkömmliche Threads jederzeit den Kontext wechseln können, was eine manuelle Konfliktlösung (unter Verwendung von Mutexen) erfordert, könnte man annehmen, dass asynchrone Bibliotheken wie Python Gevent solche Probleme automatisch lösen würden. Das ist jedoch nicht der Fall.

Auch wenn asynchrone Bibliotheken wie Gevent nicht die gleiche Komplexität wie herkömmliche präemptive Threads aufweisen, nutzen sie dennoch gemeinsam genutzte Ressourcen, auf die verschiedene Greenlets zugreifen. Dies kann zu subtilen, manchmal schwer zu erkennenden Problemen führen.

Batrachten wir folgenden Code:

def withdraw(self, amount):
	if self.balance >= amount: 
		withdraw_from_db(amount) 
		self.balance -= amount

Angenommen, der obige Code wird von mehreren Greenlets aufgerufen und withdraw_from_db() ist ein Aufruf, der die Exekutionshohheit abgibt. Da self.balance eine gemeinsam genutzte Ressource ist, ist es gut möglich, dass die Abhebung mehrfach erfolgt, auch wenn der Saldo nicht ausreicht. Das Problem ist, dass der Kontostand gelesen wird und ein Kontextwechsel stattfinden kann, so dass der zweite self.balance, den wir lesen, möglicherweise nicht derselbe ist wie der erste.

Es gibt eine native Lock-Unterstützung in Gevent. Aber ich würde vorschlagen, dass die Art und Weise, wie auf gemeinsame Ressourcen zugegriffen wird, so gestaltet wird, dass dies so weit wie möglich minimiert wird. Denn jedes Mal, wenn eine Sperre verwendet wird, blockiert dies die Ausführung anderer Greenlets, was genau das Gegenteil von Concurrency ist. Außerdem ist es sehr wahrscheinlich, dass die Sperre verwendet wird, wenn ein blockierender Aufruf vorliegt, ein Rezept für eine Katastrophe. Zum Beispiel, könnte die Abhebungslogik mithilfe einer Datenbanktransaktion implementiert werden und self.balance aus dem Code entfernen. Es wird auch andere Möglichkeiten geben, dieses Problem zu umgehen, jeder Ansatz muss jedoch sorgfältig durchdacht werden.

Fazit

Wie jedes Framework hat auch Gevent seine Vor und Nachteile. Es bietet zwar den Vorteil der asynchronen Programmierung mit weniger Komplexität als herkömmliches Multithreading, ist aber kein Allheilmittel für alle Concurreny Probleme. Es ist wichtig, diese Einschränkungen und möglichen Fallstricke zu beachten, um Gevent in Ihren Anwendungen so effektiv wie möglich zu nutzen. Teilen Sie uns auf Discord mit, welche Fallstricke Sie bei Gevent erlebt haben.

Ihr größtes Werk
steht vor der Tür

Kostenloser Test
Discord
© 2025 Platform.sh. All rights reserved.