Node.js auf Mehrkernmaschinen

606

Node.js sieht interessant aus, ABER ich muss etwas verpassen - ist Node.js nicht nur für die Ausführung auf einem einzelnen Prozess und Thread ausgelegt?

Wie skaliert es dann für Multi-Core-CPUs und Multi-CPU-Server? Immerhin ist es großartig, einen Single-Thread-Server so schnell wie möglich zu machen, aber für hohe Lasten würde ich mehrere CPUs verwenden wollen. Das Gleiche gilt für die Beschleunigung von Anwendungen - heute werden anscheinend mehrere CPUs verwendet und die Aufgaben parallelisiert.

Wie passt Node.js in dieses Bild? Ist es die Idee, mehrere Instanzen irgendwie zu verteilen, oder was?

zaharpopov
quelle
4
Es sieht so aus, als würde Ryah langsam ernsthaft damit beginnen, die integrierte Multi-Core-Unterstützung in den Knoten aufzunehmen: github.com/joyent/node/commit/…
broofa
2
PM2-Prozessmanager verwenden das Cluster-Modul intern, um Ihre NodeJS-Apps auf alle verfügbaren Kerne zu verteilen: github.com/Unitech/pm2
Unitech
@broofa, Das sind keine echten Threads und untergeordnete Prozesse haben keinen gemeinsamen Speicher. Siehe auch Was ist das Nodejs-Äquivalent zu Javas echtem Threading und flüchtig-statischen Variablen? .
Pacerier

Antworten:

697

[ Dieser Beitrag ist ab dem 02.09.2012 auf dem neuesten Stand (neuer als oben). ]]

Node.js skaliert absolut auf Multi-Core-Maschinen.

Ja, Node.js ist ein Thread pro Prozess. Dies ist eine sehr bewusste Entwurfsentscheidung und macht die Sperrsemantik überflüssig. Wenn Sie damit nicht einverstanden sind, wissen Sie wahrscheinlich noch nicht, wie wahnsinnig schwierig es ist, Multithread-Code zu debuggen. Eine ausführlichere Erklärung des Node.js-Prozessmodells und warum es so funktioniert (und warum es NIEMALS mehrere Threads unterstützt), finden Sie in meinem anderen Beitrag .

Wie nutze ich meine 16-Core-Box?

Zwei Wege:

  • Für große, schwere Rechenaufgaben wie die Bildcodierung kann Node.js untergeordnete Prozesse starten oder Nachrichten an zusätzliche Arbeitsprozesse senden. In diesem Entwurf muss ein Thread den Ablauf von Ereignissen und N Prozessen verwalten, die schwere Rechenaufgaben ausführen und die anderen 15 CPUs zerkauen.
  • Um den Durchsatz auf einem Webservice zu skalieren, sollten Sie mehrere Node.js-Server auf einer Box ausführen, einen pro Kern, und den Anforderungsverkehr zwischen ihnen aufteilen. Dies bietet eine hervorragende CPU-Affinität und skaliert den Durchsatz nahezu linear mit der Kernanzahl.

Skalieren des Durchsatzes auf einem Webservice

Seit v6.0.X hat Node.js das Cluster-Modul sofort integriert, wodurch es einfach ist, mehrere Node-Worker einzurichten, die einen einzelnen Port überwachen können. Beachten Sie, dass dies NICHT dasselbe ist wie das ältere Learnboost-Cluster-Modul, das über npm verfügbar ist .

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  http.Server(function(req, res) { ... }).listen(8000);
}

Die Mitarbeiter konkurrieren um die Annahme neuer Verbindungen, und der am wenigsten belastete Prozess wird höchstwahrscheinlich gewinnen. Es funktioniert ziemlich gut und kann den Durchsatz auf einer Multi-Core-Box recht gut skalieren.

Wenn Sie genug Last haben, um sich um mehrere Kerne zu kümmern, sollten Sie noch ein paar Dinge tun:

  1. Führen Sie Ihren Node.js-Dienst hinter einem Webproxy wie Nginx oder Apache aus - etwas, das die Verbindungsdrosselung durchführen kann (es sei denn, Sie möchten, dass die Überlastungsbedingungen die Box vollständig herunterfahren), URLs neu schreiben, statischen Inhalt bereitstellen und andere Unterdienste vertreten.

  2. Recyceln Sie regelmäßig Ihre Arbeitsprozesse. Bei einem lang laufenden Prozess summiert sich möglicherweise sogar ein kleiner Speicherverlust.

  3. Protokollsammlung / -überwachung einrichten


PS: Es gibt eine Diskussion zwischen Aaron und Christopher in den Kommentaren eines anderen Beitrags (zum Zeitpunkt dieses Schreibens ist es der oberste Beitrag). Ein paar Kommentare dazu:

  • Ein Shared-Socket-Modell ist sehr praktisch, damit mehrere Prozesse einen einzelnen Port überwachen und um die Annahme neuer Verbindungen konkurrieren können. Konzeptionell könnte man sich vorstellen, dass Apache dies mit der erheblichen Einschränkung tut, dass jeder Prozess nur eine einzige Verbindung akzeptiert und dann stirbt. Der Effizienzverlust für Apache ist mit dem Verzweigen neuer Prozesse verbunden und hat nichts mit den Socket-Operationen zu tun.
  • Für Node.js ist es eine äußerst vernünftige Lösung, wenn N-Mitarbeiter an einem einzigen Socket miteinander konkurrieren. Die Alternative besteht darin, ein On-Box-Front-End wie Nginx einzurichten und diesen Proxy-Verkehr an die einzelnen Mitarbeiter weiterzuleiten, wobei zwischen Arbeitnehmern gewechselt wird, um neue Verbindungen zuzuweisen. Die beiden Lösungen weisen sehr ähnliche Leistungsmerkmale auf. Und da Sie, wie oben erwähnt, wahrscheinlich Nginx (oder eine Alternative) haben möchten, das Ihren Knotendienst bedient, haben Sie hier wirklich die Wahl zwischen:

Gemeinsame Ports: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)

vs.

Einzelne Ports: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}

Die Einrichtung der einzelnen Ports bietet möglicherweise einige Vorteile (möglicherweise weniger Kopplung zwischen Prozessen, differenziertere Entscheidungen zum Lastenausgleich usw.), aber die Einrichtung ist definitiv aufwändiger und das integrierte Cluster-Modul ist niedrig -Komplexitätsalternative, die für die meisten Menschen funktioniert.

Dave Dopson
quelle
1
Können Sie Ratschläge zum Ausführen verschiedener NodeJS-basierter Dienste auf einer Box geben? Angenommen, ich habe 1 Server und möchte myservice1.js auf CpuCore1 und myservice2.js auf CpuCore2 ausführen. Kann ich dafür Cluster verwenden? oder ist es nur nützlich, um geklonte Dienste zu erstellen?
UpTheCreek
6
Sie sollten eine Frage dazu posten! (und ich werde diesen Kommentar als Ihre erste Antwort kopieren). Was Sie tun möchten, ist eigentlich wirklich sehr einfach. Sie würden "Cluster" nicht wirklich benötigen, sondern nur zwei verschiedene Knotendienste ausführen. Zwei Skripte, zwei Prozesse, zwei Ports. Sie könnten beispielsweise serviceA auf 3000 und serviceB auf 3001 abhören lassen. Jeder dieser Dienste könnte "Cluster" verwenden, um mehr als 1 Mitarbeiter zu haben und diese regelmäßig zu recyceln usw. Dann könnten Sie Nginx so konfigurieren, dass Port 80 abgehört und weitergeleitet wird der richtige Dienst basierend auf dem eingehenden "Host" -Header und / oder dem URL-Pfad.
Dave Dopson
1
Vielen Dank. Ich habe bereits eine verwandte Frage gestellt - Sie haben ziemlich genau beschrieben, was ich vorhatte, aber ich bin mir nicht sicher, wie ich auf CPU-Kerne abzielen soll (wenn ich so etwas wie für immer verwende).
UpTheCreek
Tolle Antwort ddopson. Was ist der beste Weg, um zwei Knotenprozesse auf demselben Computer miteinander zu kommunizieren? Gibt es ein schnelleres Protokoll als TCP, wenn sie sich auf demselben Computer befinden?
Winduptoy
1
@ Serob_b - na ja. Das Ausführen einer Node.js-App auf mehreren Computern ist weit verbreitet. Dafür wird keine Bibliothek benötigt. Sie führen Ihren Code einfach auf mehreren Computern aus und verteilen die Last auf diese. Ihre Software so zu gestalten, dass sie skaliert (dh den Status in einer Art externem Datendienst speichert, anstatt den Status im Speicher zu behalten) - das ist Ihre Aufgabe.
Dave Dopson
45

Eine Methode wäre, mehrere Instanzen von node.js auf dem Server auszuführen und dann einen Load Balancer (vorzugsweise einen nicht blockierenden wie nginx) vor sich zu platzieren.

Chandra Sekar
quelle
36
node.js ist ungefähr so ​​schnell wie nginx, Sie könnten einen node.js Load Balancer vor Ihre node.js Server stellen, wenn Sie
möchten
26
Ryan sagte ausdrücklich, dies nicht zu tun, bis der Knoten stabiler sei. Am besten führen Sie nginx vor dem Knoten aus.
Resopollution
2
Nginx vor dem Knoten löst bestimmte Probleme nicht, z. B. wenn Sie eine In-Memory-Warteschlange haben. 2 Knoteninstanzen können nicht auf die Warteschlange des anderen zugreifen.
Resopollution
5
Außerdem unterstützt nginx HTTP 1.1 nicht vollständig, sodass Dinge wie WebSockets nicht als Proxy verwendet werden können.
Ashchristopher
2
@mikeal, resopollution - Ich bin stark auf der Nginx-Seite. Ich habe Node.js mehrmals hart abgestürzt (kein Stacktrace, stirbt nur). Ich habe Nginx noch nie abgestürzt. Nginx Out-of-the-Box ist mit allen möglichen vernünftigen Drosseln konfiguriert. Node.js akzeptiert standardmäßig weiterhin neue Verbindungen, anstatt vorhandene zu bedienen, bis die Box ausfällt ... ja, die gesamte Box; Ich habe den Kernel auf einer CentOS5-Box durch Stresstest Node zum Absturz gebracht (jetzt soll DAS wirklich nicht passieren). Ich bin ein bisschen herumgekommen und sehe eine glänzende Zukunft für Node, möglicherweise einschließlich dedizierter LB-Rollen. Nur noch nicht.
Dave Dopson
30

Ryan Dahl beantwortet diese Frage in dem Tech-Vortrag, den er letzten Sommer bei Google gehalten hat . Um es zu paraphrasieren: "Führen Sie einfach mehrere Knotenprozesse aus und verwenden Sie etwas Sinnvolles, damit sie kommunizieren können, z. B. IPC im Sendmsg () - Stil oder traditioneller RPC".

Wenn Sie sich sofort die Hände schmutzig machen möchten, schauen Sie sich das spark2 Forever- Modul an. Es macht das Laichen mehrerer Knotenprozesse trivial einfach. Es übernimmt das Einrichten der Portfreigabe, sodass jeder Verbindungen zu demselben Port akzeptieren kann, und das automatische Respawning, wenn Sie sicherstellen möchten, dass ein Prozess neu gestartet wird, wenn er stirbt.

UPDATE - 10/11/11 : In der Knotengemeinschaft scheint Konsens darüber zu bestehen, dass Cluster jetzt das bevorzugte Modul für die Verwaltung mehrerer Knoteninstanzen pro Computer ist. Für immer ist auch ein Blick wert.

Broofa
quelle
8
Forever und Cluster machen sehr unterschiedliche Dinge. Sie könnten sogar beide verwenden. Für immer startet ein Prozess neu, wenn er stirbt. Der Cluster verwaltet mehrere Mitarbeiter. Sie würden Forever verwenden, um Ihren Master-Prozess zu verwalten ...
Dave Dopson
4
Außerdem wird das Learnboost-Modul weitgehend durch die in Node v0.6.x eingebrannte Version von Cluster ersetzt (Warnung: Die API-Oberfläche unterscheidet sich)
Dave Dopson,
@broofa Wie ist der Standard-IPC im Vergleich zur Verwendung von Redis oder Memcache, wenn nur Zeichenfolgen / Daten / Arrays zwischen Prozessen gesendet werden? Welcher Weg wäre schneller?
NiCk Newman
1
@broofa, IPC hat im Vergleich zu echtem Shared Memory, zu dem Java und C in der Lage sind, einen enormen Overhead.
Pacerier
@Pacerier Richtig, aber gemeinsam genutzter Speicher löst nur das Problem der Skalierung im Kontext eines einzelnen Hosts, ohne die Makroprobleme zu beheben, die für die Skalierung auf vielen Hosts erforderlich sind. Dh wie man in der Cloud läuft.
Broofa
20

Sie können das Cluster- Modul verwenden. Überprüfen Sie dies .

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}
Sergey Zhukov
quelle
13

Multi-Node nutzt alle Kerne, die Sie möglicherweise haben.
Schauen Sie sich http://github.com/kriszyp/multi-node an .

Für einfachere Anforderungen können Sie mehrere Kopien des Knotens an verschiedenen Portnummern starten und einen Load Balancer vor sie stellen.

CyberFonic
quelle
12

Node Js unterstützt Clustering, um die Vorteile Ihrer CPU voll auszuschöpfen. Wenn Sie es nicht mit Cluster ausführen, verschwenden Sie wahrscheinlich Ihre Hardwarefunktionen.

Durch Clustering in Node.js können Sie separate Prozesse erstellen, die denselben Serverport gemeinsam nutzen können. Wenn wir beispielsweise einen HTTP-Server auf Port 3000 ausführen, handelt es sich um einen Server, der auf einem einzelnen Thread auf einem einzelnen Prozessorkern ausgeführt wird.

Mit dem unten gezeigten Code können Sie Ihre Anwendung gruppieren. Dieser Code ist der offizielle Code von Node.js.

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

In diesem Artikel finden Sie das vollständige Tutorial

Toumi
quelle
11

Wie oben erwähnt, skaliert Cluster Ihre App und verteilt sie auf alle Kerne.

etwas hinzufügen wie

cluster.on('exit', function () {
  cluster.fork();
});

Startet alle fehlerhaften Worker neu.

Heutzutage bevorzugen viele Leute auch PM2 , das das Clustering für Sie übernimmt und auch einige coole Überwachungsfunktionen bietet .

Fügen Sie dann Nginx oder HAProxy vor mehreren Computern hinzu, auf denen Clustering ausgeführt wird, und Sie haben mehrere Failover-Ebenen und eine viel höhere Ladekapazität.

Will Stern
quelle
3
PM2 eignet sich hervorragend für die Produktion. Die Überwachungstools haben mir geholfen, Speicherprobleme mit Apps zu lösen.
mbokil
7

Mit der zukünftigen Version des Knotens können Sie einen Prozess verzweigen und Nachrichten an ihn weiterleiten. Ryan hat erklärt, dass er einen Weg finden möchte, um auch Dateihandler gemeinsam zu nutzen, sodass dies keine einfache Implementierung von Web Worker ist.

Derzeit gibt es keine einfache Lösung dafür, aber es ist noch sehr früh und Node ist eines der am schnellsten laufenden Open Source-Projekte, die ich je gesehen habe. Erwarten Sie also in naher Zukunft etwas Großartiges.

mikeal
quelle
7

Spark2 basiert auf Spark, das jetzt nicht mehr gepflegt wird. Cluster ist sein Nachfolger und verfügt über einige coole Funktionen, z. B. das Laichen eines Worker-Prozesses pro CPU-Kern und das Respawn toter Worker.

Der Entwickler
quelle
Die ursprüngliche Frage und viele dieser Antworten sind einige Monate alt und da sich der Knoten so schnell bewegt, schätze ich es, dass Sie den Klappentext zu Cluster hinzugefügt haben. Nachdem ich mir Cluster und seine Beispiele angesehen habe, sieht es genau so aus , wie ich (oder das OP?) Für Node möchte, danke!
Riyad Kalla
5

Ich verwende Node Worker , um Prozesse auf einfache Weise von meinem Hauptprozess aus auszuführen. Scheint großartig zu funktionieren, während wir auf den offiziellen Weg warten.

christkv
quelle
1
Warum Node Worker example.js nicht ausgeführt werden kann, ist mein Knoten 0.3.3 vor der Version
guilin 19
5

Das neue Kind auf dem Block hier ist LearnBoosts "Up" .

Es bietet "Zero-Downtime-Reloads" und erstellt zusätzlich mehrere Worker (standardmäßig die Anzahl der CPUs, aber konfigurierbar), um das Beste aus allen Welten zu bieten.

Es ist neu, scheint aber ziemlich stabil zu sein, und ich verwende es gerne in einem meiner aktuellen Projekte.

Roy
quelle
5

Mit dem Cluster- Modul können Sie alle Kerne Ihres Computers verwenden. Tatsächlich können Sie dies in nur 2 Befehlen und ohne Berühren Ihres Codes mit einem sehr beliebten Prozessmanager pm2 nutzen .

npm i -g pm2
pm2 start app.js -i max
Alister
quelle
4

Sie können Ihre Anwendung node.js auf mehreren Kernen ausführen, indem Sie das Cluster- Modul in Kombination mit dem OS- Modul verwenden, mit dem ermittelt werden kann, wie viele CPUs Sie haben.

Stellen Sie sich zum Beispiel vor, Sie haben ein serverModul, auf dem ein einfacher http-Server im Backend ausgeführt wird, und Sie möchten es für mehrere CPUs ausführen:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}

Oleksii Trekhleb
quelle
0

Es ist auch möglich, den Webdienst als mehrere eigenständige Server zu gestalten, die Unix-Sockets abhören, sodass Sie Funktionen wie die Datenverarbeitung in separate Prozesse übertragen können.

Dies ähnelt den meisten Scrpting- / Datenbank-Webserver-Architekturen, bei denen ein CGI-Prozess die Geschäftslogik verarbeitet und die Daten dann über einen Unix-Socket in eine Datenbank überträgt.

Der Unterschied besteht darin, dass die Datenverarbeitung als Knoten-Webserver geschrieben wird, der einen Port überwacht.

Es ist komplexer, aber letztendlich muss die Multi-Core-Entwicklung dahin gehen. Eine Multiprozessarchitektur mit mehreren Komponenten für jede Webanforderung.

Feuerkrähe
quelle
0

Es ist möglich, NodeJS mit einem reinen TCP-Load-Balancer (HAProxy) vor mehreren Boxen, in denen jeweils ein NodeJS-Prozess ausgeführt wird, auf mehrere Boxen zu skalieren.

Wenn Sie dann allgemeines Wissen haben, das Sie zwischen allen Instanzen teilen können, können Sie einen zentralen Redis-Speicher oder ähnliches verwenden, auf den dann von allen Prozessinstanzen aus zugegriffen werden kann (z. B. von allen Feldern aus).

Martin Tajur
quelle
Sofern diese Server keine Single-Core-CPUs enthalten, wird dadurch nicht Ihre gesamte CPU-Kapazität beansprucht (es sei denn, Sie tun auch etwas anderes).
UpTheCreek