Der Kernprozess der NodeJS-Verarbeitung einer TCP-Verbindung

Der Kernprozess der NodeJS-Verarbeitung einer TCP-Verbindung

Vor ein paar Tagen habe ich mit einem Freund etwas Wissen über Epoll und die Anforderungsverarbeitung in Node.js ausgetauscht. Lassen Sie uns heute kurz über die Logik der Node.js-Anforderungsverarbeitung sprechen. Wir beginnen mit der Listen-Funktion.

int uv_tcp_listen(uv_tcp_t* tcp, int Rückstand, uv_connection_cb cb) {
 // Legen Sie die Strategie für die Verarbeitung von Anfragen fest, siehe die Analyse unten if (single_accept == -1) {
  const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
  single_accept = (val != NULL && atoi(val) != 0); /* Standardmäßig deaktiviert. */
 }
 wenn (einzelnes_akzeptieren)
  tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
 //Bind ausführen oder Flag setzen err = maybe_new_socket(tcp, AF_INET, flags);
 // Mit dem Abhören beginnen, wenn (listen(tcp->io_watcher.fd, backlog))
  returniere UV__ERR(errno);
 // Rückruf festlegen tcp->connection_cb = cb;
 tcp->flags |= UV_HANDLE_BOUND;
 // Legen Sie den Rückruf des E/A-Watchers fest, der ausgeführt wird, wenn Epoll die Verbindung überwacht. tcp->io_watcher.cb = uv__server_io;
 // Füge die Beobachterwarteschlange ein. Zu diesem Zeitpunkt wurde sie noch nicht zu epoll hinzugefügt. Die Poll-E/A-Phase durchläuft die Beobachterwarteschlange zur Verarbeitung (epoll_ctl).
 uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);

 gebe 0 zurück;
}

Wir können sehen, dass die Libuv-Schicht der traditionellen Netzwerkprogrammierlogik folgt, wenn wir einen Server erstellen. Zu diesem Zeitpunkt wird unser Dienst gestartet. In der Poll-E/A-Phase werden unsere Abhördateideskriptoren und -kontexte (interessante Ereignisse, Rückrufe usw.) bei epoll registriert. Normalerweise wird es in Epoll blockiert. Was passiert also, wenn zu diesem Zeitpunkt eine TCP-Verbindung zustande kommt? epoll durchläuft zuerst den fd, der das Ereignis ausgelöst hat, und führt dann den Rückruf im fd-Kontext aus, d. h. uvserver_io. Werfen wir einen Blick auf uvserver_io.

void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
 // Schleifenverarbeitung, uv__stream_fd (stream) ist der dem Server entsprechende fd
 während (uv__stream_fd(stream) != -1) {
  // Holen Sie sich den FD für die Kommunikation mit dem Client über Accept. Wir können sehen, dass sich dieser FD vom FD des Servers unterscheidet. err = uv__accept(uv__stream_fd(stream));
  // Der fd, der uv__stream_fd(stream) entspricht, ist nicht blockierend. Die Rückgabe dieses Fehlers bedeutet, dass keine Verbindung zum Annehmen verfügbar ist. Direkt zurückgeben, wenn (err < 0) {
   wenn (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
    zurückkehren;
  }
  //Zeichnen Sie es auf stream->accepted_fd = err;
  // Rückruf ausführen stream->connection_cb(stream, 0);
  /*
   stream->accepted_fd ist -1, was bedeutet, dass accepted_fd im Rückruf connection_cb verbraucht wurde.
   Andernfalls melden Sie zuerst das Leseereignis des fd des Servers in epoll ab, warten Sie auf den Verbrauch und registrieren Sie es dann erneut, d. h. verarbeiten Sie die Anforderung nicht mehr */
  wenn (stream->accepted_fd != -1) {
   uv__io_stop(Schleife, &stream->io_watcher, POLLIN);
   zurückkehren;
  }
 /*
   OK, accepted_fd wurde verbraucht, möchten wir immer noch neues fd akzeptieren?
   Wenn UV_HANDLE_TCP_SINGLE_ACCEPT gesetzt ist, bedeutet dies, dass immer nur eine Verbindung gleichzeitig verarbeitet wird und dann eine Weile pausiert wird, um anderen Prozessen die Möglichkeit zu geben, sie anzunehmen (in einer Multiprozessarchitektur). Wenn es sich nicht um eine Multiprozessarchitektur handelt, stellen Sie dies erneut ein.
   Dies führt zu einer Verzögerung der Verbindungsverarbeitung*/
  wenn (Stream->Typ == UV_TCP &&
    (Stream->Flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
   Struktur Timespec Timeout = { 0, 1 };
   nanosleep(&timeout, NULL);
  }
 }
}

Von uv__server_io wissen wir, dass Libuv in einer Schleife kontinuierlich neue fd akzeptiert und dann den Rückruf ausführt. Normalerweise verbraucht der Rückruf fd und der Zyklus wird fortgesetzt, bis keine Verbindungen mehr zu verarbeiten sind. Konzentrieren wir uns als Nächstes darauf, wie fd im Rückruf verwendet wird und ob eine große Anzahl von Schleifen zu viel Zeit in Anspruch nimmt und dazu führt, dass die Libuv-Ereignisschleife für eine Weile blockiert wird. Der Rückruf von TCP ist OnConnection der C++-Schicht.

//Rückruf wird ausgelöst, wenn eine Verbindungsvorlage vorhanden ist <typename WrapType, typename UVType>
void ConnectionWrap<WrapTyp, UVTyp>::BeiVerbindung(uv_stream_t* handle,
                          int status) {
 // Holen Sie sich das C++-Layer-Objekt, das der Libuv-Struktur entspricht WrapType* wrap_data = static_cast<WrapType*>(handle->data);
 CHECK_EQ(&wrap_data->handle_, reinterpret_cast<UVType*>(handle));

 Umgebung* env = wrap_data->env();
 HandleScope handle_scope(env->isolate());
 Kontext::Bereich context_scope(env->context());

 //Objekt zur Kommunikation mit dem Client Local<Value> client_handle;

 wenn (Status == 0) {
  // Instanziieren Sie das Client-JavaScript-Objekt und den Handle.
  // Erstellen Sie eine neue JS-Ebene mit dem Objekt Local<Object> client_obj;
  wenn (!WrapType::Instantiate(env, wrap_data, WrapType::SOCKET)
       .ToLocal(&client_obj))
   zurückkehren;

  // Das Client-JavaScript-Objekt auspacken.
  WrapType* Wrap;
  // Speichern Sie das C++-Layer-Objekt, das dem von der JS-Ebene verwendeten Objekt client_obj entspricht, in Wrap ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj);
  // Den entsprechenden Handle holen
  uv_stream_t* Client = reinterpret_cast<uv_stream_t*>(&wrap->handle_);
  // Nimm eines der von handleAccept empfangenen FDS und speichere es im Client, damit der Client mit dem Client kommunizieren kann, wenn (uv_accept(handle, client))
   zurückkehren;
  Client-Handle = Client-Objekt;
 } anders {
  client_handle = Undefiniert(env->isolate());
 }
 // Callback js, client_handle entspricht der Ausführung von neuem TCP in der js-Ebene
 Lokal<Wert> argv[] = { Integer::Neu(env->isolate(), status), client_handle };
 : Wrap_Data->MakeCallback(Umgebung->onconnection_string(), Arraygröße(argv), argv);
}

Der Code sieht kompliziert aus, wir müssen uns nur auf uv_accept konzentrieren. Der erste Parameter von uv_accept ist der Handle, der dem Server entspricht, und der zweite ist das Objekt, das die Kommunikation mit dem Client darstellt.

int uv_accept(uv_stream_t* Server, uv_stream_t* Client) {
 int err;

 Schalter (Client->Typ) {
  Fall UV_NAMED_PIPE:
  Fall UV_TCP:
   // fd auf Client setzen err = uv__stream_open(client,
              Server->accepted_fd,
              UV_HANDLE_LESBAR | UV_HANDLE_SCHREIBBAR);
   brechen;
 // ...
 }

 Client->Flags |= UV_HANDLE_BOUND;
 // Markieren Sie, dass fd verbraucht wurde
 server->accepted_fd = -1;
 Rückgabefehler;
}

uv_accept hat hauptsächlich zwei Logiken: Festlegen des fd für die Kommunikation mit dem Client und Markieren als verbraucht, wodurch die gerade erwähnte while-Schleife zur weiteren Ausführung veranlasst wird. Für die obere Schicht ist es ein mit dem Client verknüpftes Objekt. In der Libuv-Schicht ist es eine Struktur, in der C++-Schicht ist es ein C++-Objekt und in der JS-Schicht ist es ein JS-Objekt. Die drei sind gekapselt und Schicht für Schicht verknüpft. Der Kern ist das fd in der Libuv-Client-Struktur, das das zugrunde liegende Ticket für die Kommunikation mit dem Client darstellt. Abschließend wird die js-Schicht zurückgerufen, welche die Onconnection von net.js ausführen soll. onconnection kapselt ein Socket-Objekt zur Darstellung der Kommunikation mit dem Client. Es enthält das Objekt der C++-Schicht, das Objekt der C++-Schicht enthält die Libuv-Struktur und die Libuv-Struktur enthält fd.

const socket = neuer Socket({
  Griff: ClientHandle,
  allowHalfOpen: self.allowHalfOpen,
  pauseOnCreate: self.pauseOnConnect,
  lesbar: wahr,
  beschreibbar: true
 });

Dies ist das Ende dieses Artikels über den Kernprozess der Verarbeitung von TCP-Verbindungen durch NodeJS. Weitere relevante Inhalte zur Verarbeitung von TCP-Verbindungen durch NodeJS finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • So verwenden Sie Node.js zum Implementieren von Befehlszeilenspielen
  • Nodejs ermöglicht das Teilen kleiner Spiele mit mehreren Personen, die gleichzeitig online die Maus bewegen
  • Implementierung einer Multiplayer-Gameserver-Engine mit Node.js
  • Node.js-Framework für Echtzeit-Multiplayer-Spiele
  • Ist node.js für die Entwicklung von Spiele-Backends geeignet?
  • Ein vollständiges Beispiel für die Implementierung eines zeitgesteuerten Crawlers mit Nodejs
  • Unterschiede zwischen diesem Schlüsselwort in NodeJS und Browsern
  • So schreiben Sie eine Node.JS-Version eines Spiels

<<:  Beheben Sie das Fehlerproblem, das durch die Änderung von MySQL data_dir verursacht wird

>>:  So verwenden Sie Docker-Container zur Implementierung der Proxy-Weiterleitung und Datensicherung

Artikel empfehlen

CSS-Isolationsproblem in Blazor

1. Umwelt VS 2019 16.9.0 Vorschau 1.0 .NET SDK 5....

Analyse des Konfigurationsprozesses der Nginx-HTTP-Integritätsprüfung

Passive Prüfung Mit passiven Integritätsprüfungen...

Erfahrungsaustausch über die Priorität des Ladens von CSS-Stilen

Während der Projektentwicklung bin ich gestern auf...

Mysql 5.7.17 Winx64-Installationstutorial auf Win7

Softwareversion und Plattform: MySQL-5.7.17-winx6...

Eine kurze Erläuterung der Schriftarteinstellungen in Webseiten

Das Festlegen der Schriftart für die gesamte Site...

XHTML-Tutorial für die ersten Schritte: XHTML-Webseiten-Bildanwendung

<br />Das sinnvolle Hinzufügen von Bildern k...

So überwachen Sie den Ausführungsstatus eines Docker-Container-Shell-Skripts

Szenario Das Unternehmensprojekt wird in Docker b...

Detaillierte Einführung in den DOCTYPE-Typ

<br />Wir deklarieren DOCTYPE in HTML normal...

Docker löscht private Bibliotheksbilder vollständig

Werfen wir zunächst einen Blick auf die allgemein...

4 Lösungen für CSS-Browserkompatibilitätsprobleme

Frontend ist ein harter Job, nicht nur weil sich ...

So installieren Sie Django in einer virtuellen Umgebung unter Ubuntu

Führen Sie die folgenden Vorgänge im Ubuntu-Befeh...