Heim  >  Artikel  >  Web-Frontend  >  Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

青灯夜游
青灯夜游nach vorne
2021-04-19 10:08:062074Durchsuche

Dieser Artikel vermittelt Ihnen ein vorläufiges Verständnis der asynchronen E/A in Nodejs. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Der Begriff „asynchron“ wurde tatsächlich vor Node geboren. In den meisten höheren Programmiersprachen ist Asynchronität jedoch selten. Unter vielen Hochsprachen oder Betriebsplattformen ist Node der erste, der Asynchronität als Hauptprogrammiermethode und Designkonzept verwendet. [Verwandte Empfehlung: „nodejs Tutorial“]

Asynchrone E/A, ereignisgesteuert und Single-Threaded bilden die Basis von Node, und die ereignisgesteuerten und asynchronen E/A-Designkonzepte von Nginx und Node sind relativ ähnlich . Nginx ist in reinem C geschrieben, bietet eine hervorragende Leistung und verfügt über die leistungsstarke Fähigkeit, Verbindungen für Clients zu verwalten, ist jedoch immer noch durch verschiedene Synchronisierungsprogrammiersprachen eingeschränkt. Aber Node ist vielseitig einsetzbar. Er kann als Server verwendet werden, um eine große Anzahl gleichzeitiger Anforderungen von Clients zu verarbeiten, und er kann auch als Client verwendet werden, um gleichzeitige Anforderungen an verschiedene Anwendungen im Netzwerk zu stellen.

Warum asynchrone E/A in Node so wichtig ist, liegt daran, dass Node für das Netzwerk konzipiert ist und Parallelität zu einer Standardausrüstung in der modernen Programmierung geworden ist.


Benutzererfahrung

In „Hochleistungs-JavaScript“ wird erwähnt, dass der Benutzer das Gefühl hat, dass die Seite nicht mehr reagiert, wenn die Ausführungszeit des Skripts 100 Millisekunden überschreitet. Im B/S-Modell verursachen Netzwerkgeschwindigkeitsbeschränkungen große Probleme für das Echtzeiterlebnis von Webseiten. Wenn die Webseite vorübergehend eine Ressource abrufen und diese synchron abrufen muss, muss JavaScript warten, bis die Ressource vollständig vom Server abgerufen wurde, bevor die Ausführung fortgesetzt werden kann. Während dieser Zeit pausiert die Benutzeroberfläche und reagiert nicht die Interaktion des Benutzers. Diese Benutzererfahrung wird äußerst schlecht sein. Bei asynchronen Anforderungen befindet sich die Ausführung von JavaScript und der Benutzeroberfläche beim Herunterladen von Ressourcen nicht im Wartezustand und kann weiterhin auf Benutzerinteraktionen reagieren.

In ähnlicher Weise kann das Front-End die Blockierung der Benutzeroberfläche durch asynchronen Betrieb beseitigen, aber die Geschwindigkeit, mit der das Front-End Ressourcen erhält, hängt auch von der Reaktionsgeschwindigkeit des Back-Ends ab. Wenn eine Ressource aus der Rückgabe von Daten von zwei verschiedenen Standorten stammt, verbraucht die erste Ressource M Millisekunden und die zweite Ressource N Millisekunden. Wenn die Synchronisierungsmethode verwendet wird, beträgt die zum Abrufen der beiden Ressourcen benötigte Zeit M+N Millisekunden. Bei der asynchronen Methode blockiert der Erwerb der ersten Ressource nicht den Erwerb der zweiten Ressource und die verbrauchte Zeit beträgt max(M,N).

Wenn die Website oder Anwendung weiter wächst, steigen die Werte von M und N linear an, und dann ist die asynchrone Leistung besser als die synchrone.

Ressourcenzuweisung

Angenommen, dass im Geschäftsszenario eine Reihe unabhängiger Aufgaben erledigt werden müssen. Es gibt zwei gängige Methoden:

Serielle Single-Thread-Ausführung auf einmal

Parallele Multi-Thread-Ausführung Abschluss
  • Wenn erstellt Der Overhead von Multithreading ist geringer als der von paralleler Ausführung, daher wird Multithreading bevorzugt. Multithreading weist jedoch einen großen Overhead bei der Thread-Erstellung und dem Thread-Kontextwechsel während der Ausführung auf Bei der Programmierung treten häufig Probleme wie Sperren und Statussynchronisation auf.
  • Der Nachteil der sequentiellen Ausführung von Aufgaben mit einem Thread ist die Leistung. Jede etwas langsamere Aufgabe führt dazu, dass die nachfolgende Ausführung des Codes blockiert wird. Bei Computerressourcen können E/A- und CPU-Berechnungen normalerweise parallel ausgeführt werden, aber das synchrone Programmiermodell führt dazu, dass E/A auf nachfolgende Aufgaben wartet, was dazu führt, dass die Ressourcen nicht besser genutzt werden.

Node verwendet einen einzelnen Thread, um Multi-Thread-Deadlock, Zustandssynchronisierung und andere Probleme zu vermeiden. Er verwendet asynchrone E/A, um zu verhindern, dass ein einzelner Thread blockiert, und nutzt die CPU besser aus.

Asynchrone E/A-Implementierung

Asynchrone E/A wird in Node am häufigsten verwendet, ist aber nicht ursprünglich für Node.


Asynchrone E/A und nicht blockierende E/A

Für Computerkernel-E/A sind asynchron/synchron und blockierend/nicht blockierend zwei verschiedene Dinge. Das Betriebssystem verfügt nur über zwei Methoden für E/A: blockierend und nicht blockierend. Beim Aufrufen blockierender E/A muss die Anwendung warten, bis die E/A abgeschlossen ist, bevor sie das Ergebnis zurückgibt.

Ein Merkmal des Blockierens von E/A besteht darin, dass der Aufruf nach dem Aufruf warten muss, bis alle Vorgänge auf Systemkernelebene abgeschlossen sind, bevor der Aufruf beendet wird. Das Blockieren von E/A führt dazu, dass die CPU auf E/A wartet, wodurch Wartezeit verschwendet wird und die Verarbeitungsleistung der CPU nicht vollständig genutzt werden kann.

Um die Leistung zu verbessern, bietet der Kernel nicht blockierende E/A. Der Unterschied zwischen nicht blockierender E/A und blockierender E/A besteht darin, dass nach der Rückkehr der nicht blockierenden E/A die CPU-Zeitscheibe für die Verarbeitung anderer Dinge verwendet werden kann Die Leistungsverbesserung ist offensichtlich, aber aufgrund des Abschlusses ist die E/A nicht abgeschlossen und es werden nicht die von der Geschäftsschicht erwarteten Daten sofort zurückgegeben, sondern nur der aktuelle Aufrufstatus.

Um vollständige Daten zu erhalten, muss die Anwendung den E/A-Vorgang wiederholt aufrufen, um den Abschluss zu bestätigen. Diese Technik des wiederholten Aufrufs, um festzustellen, ob ein Vorgang abgeschlossen ist, wird „Polling“ genannt.

Zu den vorhandenen Polling-Technologien gehören hauptsächlich Read, Select, Poll, Epoll und Kqueue. Hier werde ich nur über das Umfrageprinzip von Epoll sprechen.

epoll ist der effizienteste E/A-Ereignisbenachrichtigungsmechanismus unter Linux. Wenn bei der Abfrage kein E/A-Ereignis erkannt wird, wird es in den Ruhezustand versetzt, bis ein Ereignis eintritt, um es aufzuwecken. Es verwendet tatsächlich Ereignisbenachrichtigungs- und Ausführungsrückrufmethoden, anstatt Abfragen zu durchlaufen, sodass keine CPU verschwendet wird und die Ausführungseffizienz höher ist.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Die Polling-Technologie erfüllt den Bedarf an nicht blockierenden E/A, um sicherzustellen, dass vollständige Daten abgerufen werden. Für das Programm handelt es sich jedoch immer noch um eine Art Synchronisierung, da die Anwendung weiterhin auf die E/A warten muss komplett zurückzugeben, was noch viel Wartezeit kostet. Während des Wartens wird die CPU entweder zum Durchlaufen von Dateideskriptoren verwendet oder sie schläft, während sie darauf wartet, dass Zeit eintrifft.

Realistische asynchrone E/A

Vervollständigt die Datenerfassung, indem einige Threads blockierende E/A oder nicht blockierende E/A plus Abfragetechnologie ausführen, sodass ein Thread die Berechnungsverarbeitung durchführen und die erhaltenen Daten über Threads kommunizieren kann by I/O wird übertragen, was die Implementierung asynchroner I/O erleichtert (obwohl dies simuliert wird)

Aber zunächst nutzte Node libeio und libev, um den I/O-Teil unter der *nix-Plattform zu implementieren und so asynchrone I/O zu erreichen. O. In Node v0.9.3 ist ein Thread-Pool implementiert, um asynchrone E/A abzuschließen.

Das IOCP unter Windows bietet bis zu einem gewissen Grad die asynchrone E/A von Li Xiang: Aufrufen asynchroner Methoden, Warten auf Benachrichtigung nach Abschluss der E/A und Ausführen von Rückrufen. Benutzer müssen keine Abfragen in Betracht ziehen. Seine Interna basieren jedoch immer noch auf dem Thread-Pool-Prinzip. Der Unterschied besteht darin, dass diese Thread-Pools vom Systemkernel verwaltet werden.

Aufgrund des Unterschieds zwischen der Windows-Plattform und der *nix-Plattform stellt Node libuv als abstrakte Kapselungsschicht bereit, sodass alle Plattformkompatibilitätsbeurteilungen von dieser Schicht vervollständigt werden und sichergestellt wird, dass der Knoten der oberen Schicht und der benutzerdefinierte Thread der unteren Schicht vorhanden sind pool und IOCP Die Zeichen sind unabhängig.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Wir erwähnen oft, dass Node Single-Threaded ist, was bedeutet, dass JavaScript in einem einzelnen Thread ausgeführt wird. In Node gibt es unabhängig davon, ob es sich um eine *nix- oder eine Windows-Plattform handelt, einen Thread-Pool, der E/A-Aufgaben intern erledigt.

Die asynchrone E/A des Knotens


vervollständigt die gesamte asynchrone E/A-Verbindung mit Ereignisschleifen, Beobachtern, Anforderungsobjekten usw.

Ereignisschleife

Die Ereignisschleife ist Nodes eigenes Ausführungsmodell, wodurch Rückruffunktionen sehr verbreitet sind.

Wenn der Prozess startet, erstellt Node eine Schleife ähnlich while(true). Jedes Mal, wenn der Schleifenkörper ausgeführt wird, nennen wir ihn Tick. Der Prozess jedes Ticks besteht darin, zu prüfen, ob ein zu verarbeitendes Ereignis vorhanden ist, und wenn ja, das Ereignis und die zugehörige Rückruffunktion abzurufen. Wenn zugehörige Rückruffunktionen vorhanden sind, führen Sie diese aus. Geben Sie dann die nächste Schleife ein und verlassen Sie den Prozess, wenn keine weiteren Ereignisse zu verarbeiten sind.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Beobachter

In jeder Ereignisschleife gibt es einen oder mehrere Beobachter. Der Prozess zur Bestimmung, ob Ereignisse verarbeitet werden müssen, besteht darin, diese Beobachter zu fragen, ob Ereignisse verarbeitet werden müssen.

In Node stammen Ereignisse hauptsächlich aus Netzwerkanforderungen, Datei-E/A usw. Zu den Beobachtern, die diesen Zeiten entsprechen, gehören Datei-E/A-Beobachter, Netzwerk-E/A-Beobachter usw. Beobachter kategorisierten Ereignisse.

Die Ereignisschleife ist ein typisches Producer/Consumer-Modell. Asynchrone E/A, Netzwerkanforderungen usw. sind die Erzeuger von Ereignissen und stellen dem Knoten ständig verschiedene Arten von Ereignissen zur Verfügung. Diese Ereignisse werden an die entsprechenden Beobachter übermittelt, und die Ereignisschleife übernimmt die Ereignisse von den Beobachtern und verarbeitet sie.

Anforderungsobjekt

Für asynchrone E/A-Aufrufe des Knotens wird die Rückruffunktion nicht vom Entwickler aufgerufen. Im Übergangsprozess von der Initiierung eines Aufrufs durch JavaScript bis zum Abschluss einer E/A-Operation durch den Kernel gibt es ein Produkt namens Anforderungsobjekt

Die fs.open()-Methode wird unten als kleines Beispiel verwendet.

fs.open = function(path,flags,mode,callback){
    //...
    binding.open(pathModule._makeLong(path),
                    stringToFlags(flags),
                    mode,
                    callback);
}

fs.open() wird verwendet, um eine Datei basierend auf dem angegebenen Pfad und den angegebenen Parametern zu öffnen, um einen Dateideskriptor zu erhalten. Dies ist die vorläufige Operation für alle nachfolgenden E/A-Vorgänge. Der Code auf JavaScript-Ebene führt Vorgänge auf niedrigerer Ebene aus, indem er das C++-Kernmodul aufruft.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

JavaScript ruft das Kernmodul von Node auf, und das integrierte Modul führt Systemaufrufe über libuv durch. Hier ist die klassische Aufrufmethode in Node. Hier dient libuv als Kapselungsschicht und verfügt über zwei Plattformimplementierungen, die im Wesentlichen die Methode uv_fs_open() aufruft. Während des Aufrufvorgangs von uv_fs_open() werden die von der JavaScript-Ebene und der aktuellen Methode übergebenen Parameter in ein Anforderungsobjekt gekapselt und die Rückruffunktion wird auf die Eigenschaften dieses Objekts festgelegt. Nachdem das Objekt gepackt wurde, wird es in den Thread-Pool verschoben, um auf die Ausführung zu warten.

An diesem Punkt kehrt der JavaScript-Aufruf sofort zurück und die erste Phase des von der JavaScript-Ebene initiierten asynchronen Aufrufs endet. JavaScript-Threads können weiterhin nachfolgende Vorgänge für die aktuelle Aufgabe ausführen.

Das Anforderungsobjekt ist ein wichtiges Zwischenprodukt im asynchronen E/A-Prozess. Alle Zustände werden in diesem Objekt gespeichert, einschließlich des Sendens an den Thread-Pool, um auf die Ausführung und Rückrufverarbeitung zu warten, nachdem der E/A-Vorgang abgeschlossen ist.

Ausführungsrückruf

Das Zusammenstellen des Anforderungsobjekts und das Senden an den E/A-Thread-Pool zur Ausführung ist nur der erste Teil des Abschlusses einer E/A, und die Rückrufbenachrichtigung ist der zweite Teil.

Nachdem die E/A-Operation im Thread-Pool aufgerufen wurde, wird das erhaltene Ergebnis im Attribut req->result gespeichert und anschließend wird PostQueueCompletionStatus() aufgerufen, um IOCP darüber zu informieren, dass die aktuelle Objektoperation abgeschlossen wurde.

Zu diesem Zeitpunkt ist der gesamte asynchrone E/A-Prozess vollständig abgeschlossen.

Ein vorläufiges Verständnis der asynchronen E/A in Nodejs

Die Ereignisschleife, der Beobachter, das Anforderungsobjekt und der E/A-Thread-Pool bilden zusammen die Grundelemente des asynchronen E/A-Modells des Knotens.

Zusammenfassung

Nachdem wir es geklärt haben, können wir mehrere Schlüsselwörter für asynchrone E/A extrahieren: Einzelthread, Ereignisschleife, Beobachter und E/A-Thread-Pool. Ein einzelner Thread und ein Thread-Pool scheinen ein gewisses Paradoxon zu sein. Da JavaScript Single-Threaded ist, ist es leicht zu verstehen, dass es die Vorteile von Multi-Core-CPUs nicht voll ausnutzen kann. Tatsächlich ist Node selbst in Node, abgesehen davon, dass JavaScript Single-Threaded ist, tatsächlich Multi-Threaded, aber der E/A-Thread verbraucht weniger CPU. Abgesehen davon, dass Benutzercode nicht parallel ausgeführt werden kann, können alle E/A (Festplatten-E/A, Netzwerk-E/A usw.) parallel ausgeführt werden.

Weitere Kenntnisse zum Thema Programmierung finden Sie unter: Programmiervideos! !

Das obige ist der detaillierte Inhalt vonEin vorläufiges Verständnis der asynchronen E/A in Nodejs. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Vorheriger Artikel:So führen Sie CSS in JS einNächster Artikel:So führen Sie CSS in JS ein