614 Lesungen
614 Lesungen

Asynchrone Laufzeiten: Eine Einführung für den verwirrten Entwickler

von Oleg Efimov3m2024/04/04
Read on Terminal Reader
Read this story w/o Javascript

Zu lang; Lesen

Diese Vorlage zielt darauf ab, zu beleuchten, wie asynchrone Programmierung Anwendungsleistung, Skalierbarkeit und Reaktionsfähigkeit erhöhen kann.Indem wir uns auf die universellen Prinzipien von nicht-blockierenden Operationen und Ereignislängen konzentrieren, gehen wir über spezifische Technologien wie AsyncIO, Node.js oder Go hinaus.
featured image - Asynchrone Laufzeiten: Eine Einführung für den verwirrten Entwickler
Oleg Efimov HackerNoon profile picture

Diese Einführung richtet sich an Entwickler, die zwischen Neugier und beruflichem Wachstum streben. Sie zeigt, wie asynchrone Programmierung die Leistung, Skalierbarkeit und Reaktionsfähigkeit von Anwendungen verbessern kann. Mit dem Fokus auf die universellen Prinzipien nichtblockierender Operationen und Ereignisschleifen gehen wir über spezifische Technologien wie AsyncIO, Node.js oder Go hinaus.


Diese Erkundung richtet sich an Softwareentwickler, die die Effizienz moderner Software verstehen möchten, ohne sich mit der Komplexität des technischen Fachjargons auseinandersetzen zu müssen.


Stellen Sie sich einen einzelnen Thread, der Anfragen bearbeitet, wie eine einspurige Straße in einer geschäftigen Stadt vor: Jede Anfrage ist ein Auto, und wenn es nur eine Spur gibt, stauen sich die Autos und verursachen Verzögerungen. Doch was wäre, wenn unsere Stadt – unser Service – den Verkehr intelligenter steuern könnte? Hier kommt die Magie einer asynchronen Laufzeit ins Spiel. Es ist, als würden wir unserer Stadt ein intelligentes Verkehrssystem hinzufügen, das Autos ohne Wartezeiten durch mehrere Fahrspuren und Kreuzungen leitet.


Dieses System sorgt für einen reibungslosen Verkehrsfluss und stellt sicher, dass kein Auto (oder keine Aufgabe) zu lange wartet. Genau auf diese Weise sorgt eine asynchrone Laufzeit dafür, dass unsere Software effizient läuft.


Betrachten wir ein Beispiel für einen Dienst, der E/A-Vorgänge synchron abschließt. Der Übersichtlichkeit halber werden diese Vorgänge im Diagramm außerhalb des Hauptausführungsflusses angezeigt:

Das Blockieren von E/A-Vorgängen während der Startphase Ihres Dienstes kann akzeptabel sein. Bei der Verarbeitung externer Anfragen sollten Sie diesen Ansatz jedoch vermeiden. Das folgende Diagramm veranschaulicht, wie die Diensteffizienz durch die Einführung nicht blockierender E/A-Vorgänge verbessert werden kann:

Diese Beispiele veranschaulichen Szenarien, in denen die Leistungssteigerung des Servers maximiert wird. Der Vorteil nicht blockierender Operationen bleibt jedoch unabhängig von der Häufigkeit eingehender Anfragen bestehen. Selbst unter nicht optimalen Bedingungen profitiert die Leistung durch die Integration dedizierter E/A-Threads in den Verarbeitungsworkflow.


Die Gesamtzeit für die Bearbeitung einer Anfrage (von der ersten Anfrage des Clients bis zur endgültigen Antwort, dargestellt durch die blaue Zahl rechts) verringert sich unweigerlich, sofern genügend Threads für die Bearbeitung aller Anfragen vorhanden sind. Im schlimmsten Fall überschreitet diese Dauer nicht die synchroner Verarbeitungsmethoden.

Wir stoßen dann auf eine Vorgehensweise, die vielen Entwicklern möglicherweise kontraintuitiv erscheint. Wenn I/O-Operationen einen erheblichen Teil der Anfrageverarbeitungszeit ausmachen, kann die Optimierung anderer Codesegmente zu einer leichten Verbesserung führen. Die Dauer des Datenabrufs aus einem Cache kann eng mit der Zeit übereinstimmen, die für Geschäftslogik und Vorlagen-Rendering aufgewendet wird.


Durch den Einsatz von Caching oder einer In-Process-Datenbank können die Datenabrufzeiten im Vergleich zu anderen Verarbeitungsaktivitäten reduziert werden.


Um Code effektiv zu segmentieren und die Ausführung von Callbacks zu erleichtern, kann die Laufzeitumgebung angewiesen werden, mit dem nächsten Ereignisschleifenzyklus fortzufahren. Nachfolgend sehen Sie ein anschauliches Beispiel für die Anwendung dieses Konzepts:

 // blocking callbacks function func1_cb(str, cb) { var res = func1(str); cb(res); } function func2_cb(str, cb) { var res = func2(str); cb(res); } // non-blocking callbacks function func1_cb(str, cb) { var res = func1(str); process.nextTick(function () { cb(res); }); } function func2_cb(str, cb) { var res = func2(str); process.nextTick(function () { cb(res); }); } // usage example func1_cb(content, function (str) { func2_cb(str, function (result) { // work with result }); });

Durch die Anwendung dieser Methode zur Aufteilung der Berechnungen in zwei Teile bleibt die Gesamtverarbeitungszeit für Szenarien mit nahezu gleichzeitigen Anfragen gleich. Die Antwort auf die erste Anfrage verzögert sich jedoch:

Dieses Szenario stellt das ungünstigste Ergebnis bei der Anwendung der „Next Tick“-Strategie dar. Wie das erste Beispiel zeigt, erweist sich die „Next Tick“-Methode bei seltenen Anfragen als vorteilhaft. Bei mäßig häufig eingehenden Anfragen erhöht diese Technik die Verarbeitungsgeschwindigkeit, da die Initiierung neuer Anfragen und der Start nicht blockierender Operationen während Ausführungspausen verschachtelt werden können. Dieser Ansatz reduziert effektiv sowohl die Gesamtverarbeitungszeit als auch die durchschnittliche Zeit pro Anfrage:

Zusammenfassend lässt sich sagen, dass die Einführung nicht blockierender I/O entscheidend zur Verbesserung der Anwendungsleistung beiträgt und in Umgebungen mit geringem und hohem Anforderungsaufkommen von Vorteil ist. Darüber hinaus verbessert eine effektive Sequenzierung des Ausführungsflusses – veranschaulicht durch Konzepte wie die „Next Tick“-Technik – die Servereffizienz erheblich. Die Anwendung dieser asynchronen Programmierpraktiken bietet klare Vorteile gegenüber herkömmlichen, synchronen Methoden.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks