Node.js baut auf libuv eine plattformübergreifende Bibliothek, die Apis/Syscalls für asynchrone (nicht blockierende) Ein-/Ausgabe abstrahiert, die von den unterstützten Betriebssystemen (Unix, OS X und Windows) bereitgestellt werden.
Asynchrone IO
In diesem Programmiermodell werden Öffnungs-/Lese-/Schreibvorgänge auf Geräten und Ressourcen (Sockets, Dateisystem usw.), die vom Dateisystem verwaltet werden, durchgeführt. den aufrufenden Thread nicht blockieren (wie im typischen synchronen c-ähnlichen Modell) und markieren Sie einfach den Prozess (in der Datenstruktur auf Kernel-/OS-Ebene), um benachrichtigt zu werden, wenn neue Daten oder Ereignisse verfügbar sind. Im Falle einer Webserver-ähnlichen Anwendung ist der Prozess dann dafür verantwortlich, herauszufinden, zu welcher Anfrage/welchem Kontext das benachrichtigte Ereignis gehört und die Anfrage von dort aus weiter zu verarbeiten. Beachten Sie, dass dies zwangsläufig bedeutet, dass Sie sich auf einem anderen Stack-Frame befinden als derjenige, der die Anfrage an das Betriebssystem gestellt hat, da letzterer einem Prozess-Dispatcher weichen musste, damit ein Single-Thread-Prozess neue Ereignisse bearbeiten kann.
Das Problem mit dem von mir beschriebenen Modell ist, dass es dem Programmierer nicht vertraut und schwer zu erklären ist, da es nicht sequentiell ist. "Sie müssen eine Anfrage in Funktion A stellen und das Ergebnis in einer anderen Funktion verarbeiten, in der Ihre lokalen Daten aus A normalerweise nicht verfügbar sind.
Knotenmodell (Fortsetzungsübergabe-Stil und Ereignisschleife)
Node geht das Problem an, indem es die Sprachfunktionen von Javascript nutzt, um dieses Modell etwas synchroner zu gestalten, indem es den Programmierer dazu veranlasst, einen bestimmten Programmierstil zu verwenden. Jede Funktion, die IO anfordert, hat eine Signatur wie function (... parameters ..., callback)
und muss einen Rückruf erhalten, der aufgerufen wird, wenn die angeforderte Operation abgeschlossen ist (bedenken Sie, dass die meiste Zeit damit verbracht wird, auf das Betriebssystem zu warten, um die Fertigstellung zu signalisieren - Zeit, die für andere Arbeiten genutzt werden kann). Javascript's Unterstützung für Closures erlaubt es Ihnen, Variablen, die Sie in der äußeren (aufrufenden) Funktion definiert haben, innerhalb des Körpers des Callbacks zu verwenden - dies erlaubt es, den Status zwischen verschiedenen Funktionen zu halten, die von der Node-Laufzeit unabhängig voneinander aufgerufen werden. Siehe auch Fortsetzung des Passspiels .
Außerdem wird die aufrufende Funktion nach dem Aufruf einer Funktion, die eine IO-Operation auslöst, normalerweise return
Steuerung zum Knoten Ereignisschleife . Diese Schleife ruft den nächsten Callback oder die nächste Funktion auf, die für die Ausführung vorgesehen war (höchstwahrscheinlich, weil das entsprechende Ereignis vom Betriebssystem gemeldet wurde) - dies ermöglicht die gleichzeitige Verarbeitung mehrerer Anfragen.
Sie können sich die Ereignisschleife von node wie folgt vorstellen ähnlich wie der Dispatcher des Kernels Der Kernel würde einen blockierten Thread zur Ausführung einplanen, sobald sein anstehendes IO abgeschlossen ist, während der Knoten einen Rückruf einplant, wenn das entsprechende Ereignis eingetreten ist.
Hochgradig konkurrierend, keine Parallelität
Abschließend lässt sich sagen, dass der Satz "alles läuft parallel, außer Ihrem Code" den Punkt gut wiedergibt, dass Node Ihrem Code erlaubt, Anfragen von Hunderttausende von offenen Sockets mit einem einzigen Thread durch Multiplexing und Sequenzierung all Ihrer js-Logik in einem einzigen Ausführungsstrom (auch wenn die Aussage "alles läuft parallel" hier wahrscheinlich nicht korrekt ist - siehe Gleichzeitigkeit vs. Parallelität - Was ist der Unterschied? ). Dies funktioniert ziemlich gut für Webapp-Server, da die meiste Zeit tatsächlich auf das Warten auf Netzwerk oder Festplatte (Datenbank / Sockets) verbracht wird und die Logik ist nicht wirklich CPU-intensiv - das heißt,: dies funktioniert gut bei IO-gebundenen Arbeitslasten .