Node.js su macchine multi-core


606

Node.js sembra interessante, MA devo perdere qualcosa - Node.js non è sintonizzato solo per essere eseguito su un singolo processo e thread?

Quindi come si dimensiona per CPU multi-core e server multi-CPU? Dopotutto, è tutto perfetto per rendere il più veloce possibile un server single thread, ma per carichi elevati vorrei usare diverse CPU. E lo stesso vale per rendere le applicazioni più veloci - sembra oggi che sia più uso di più CPU e parallelismo delle attività.

Come si inserisce Node.js in questa immagine? La sua idea è quella di distribuire in qualche modo più istanze o cosa?


4
Sembra che Ryah stia iniziando a prendere sul serio l'inclusione del supporto multi-core integrato nel nodo: github.com/joyent/node/commit/…
broofa,

2
Il gestore processi PM2 utilizza il modulo cluster internamente per diffondere le app NodeJS su tutti i core disponibili: github.com/Unitech/pm2
Unitech

@broofa, quelli non sono thread reali e i processi figlio non hanno memoria condivisa. Vedi anche Qual è l'equivalente di Nodejs del threading reale di Java e delle variabili volatile-statiche? .
Pacerier,

Risposte:


697

[ Questo post è aggiornato al 2012-09-02 (più recente di quanto sopra). ]

Node.js è assolutamente scalabile su macchine multi-core.

Sì, Node.js è un thread per processo. Questa è una decisione di progettazione molto deliberata ed elimina la necessità di gestire la semantica di blocco. Se non sei d'accordo, probabilmente non ti rendi ancora conto di quanto sia follemente difficile eseguire il debug del codice multi-thread. Per una spiegazione più approfondita del modello di processo Node.js e perché funziona in questo modo (e perché non supporterà MAI più thread), leggi il mio altro post .

Quindi, come posso sfruttare la mia scatola a 16 core?

Due strade:

  • Per attività di calcolo pesanti come la codifica delle immagini, Node.js può attivare processi figlio o inviare messaggi a processi di lavoro aggiuntivi. In questo progetto, avresti un thread che gestisce il flusso di eventi e N processi che svolgono compiti di calcolo pesanti e masticano le altre 15 CPU.
  • Per ridimensionare il throughput su un servizio Web, è necessario eseguire più server Node.js su un box, uno per core e dividere il traffico delle richieste tra di loro. Ciò fornisce un'eccellente affinità con la CPU e ridimensionerà il throughput in modo quasi lineare con il conteggio dei core.

Ridimensionamento della velocità effettiva su un servizio Web

Poiché v6.0.X Node.js ha incluso il modulo cluster immediatamente, il che semplifica la configurazione di più nodi di lavoro che possono ascoltare su una singola porta. Si noti che questo NON è lo stesso del vecchio modulo "cluster" di learnboost disponibile tramite npm .

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

I lavoratori competeranno per accettare nuove connessioni e il processo meno caricato avrà più probabilità di vincere. Funziona abbastanza bene e può aumentare abbastanza bene la velocità su una scatola multi-core.

Se hai abbastanza carico per occuparti di più core, allora vorrai fare anche qualche altra cosa:

  1. Esegui il tuo servizio Node.js dietro un proxy web come Nginx o Apache - qualcosa che può limitare la connessione (a meno che non desideri che le condizioni di sovraccarico riducano completamente la casella), riscrivi gli URL, offri contenuto statico e proxy altri servizi secondari.

  2. Ricicla periodicamente i tuoi processi di lavoro. Per un processo di lunga durata, alla fine si sommerà anche una piccola perdita di memoria.

  3. Raccolta / monitoraggio del registro di installazione


PS: C'è una discussione tra Aaron e Christopher nei commenti di un altro post (al momento della stesura di questo, è il primo post). Alcuni commenti al riguardo:

  • Un modello di socket condiviso è molto conveniente per consentire a più processi di ascoltare su una singola porta e competere per accettare nuove connessioni. Concettualmente, potresti pensare ad Apache preforked che lo fa con l'importante avvertimento che ogni processo accetterà solo una singola connessione e poi morirà. La perdita di efficienza per Apache è nell'overhead del fork di nuovi processi e non ha nulla a che fare con le operazioni del socket.
  • Per Node.js, avere N lavoratori in competizione su un singolo socket è una soluzione estremamente ragionevole. L'alternativa è impostare un front-end integrato come Nginx e disporre di quel traffico proxy per i singoli lavoratori, alternando i lavoratori per l'assegnazione di nuove connessioni. Le due soluzioni hanno caratteristiche prestazionali molto simili. E poiché, come ho già detto, probabilmente vorrai avere Nginx (o un'alternativa) in grado di supportare il tuo nodo in ogni caso, la scelta qui è davvero tra:

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

vs

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

Ci sono probabilmente alcuni vantaggi nell'impostazione delle singole porte (potenziale per avere meno accoppiamento tra processi, prendere decisioni più sofisticate sul bilanciamento del carico, ecc.), Ma è sicuramente più lavoro da impostare e il modulo cluster integrato è basso alternativa di complessità che funziona per la maggior parte delle persone.


1
puoi offrire qualche consiglio per eseguire diversi servizi basati su nodejs su una casella? Ad esempio, supponiamo di avere 1 server e di voler eseguire myservice1.js su CpuCore1 e myservice2.js su CpuCore2. Posso usare il cluster per questo? o è utile solo per creare servizi clonati?
UpTheCreek,

6
Dovresti pubblicare una domanda per questo! (e copierò questo commento come prima risposta). Quello che vuoi fare è davvero molto semplice. Non avresti davvero bisogno di "cluster", avresti semplicemente eseguito due diversi servizi di nodo. Due script, due processi, due porte. Ad esempio, potresti avere serviceA in ascolto su 3000 e serviceB in ascolto su 3001. Ognuno di questi servizi potrebbe utilizzare "cluster" per avere più di 1 lavoratore e riciclarli periodicamente, ecc. Quindi puoi configurare Nginx per ascoltare sulla porta 80 e inoltrare a il servizio corretto basato sull'intestazione "Host" in entrata e / o sul percorso dell'URL.
Dave Dopson,

1
Grazie. Ho già pubblicato una domanda correlata : hai descritto praticamente ciò che avevo in mente, ma non sono sicuro di come scegliere i core della CPU (quando usi qualcosa del genere per sempre).
UpTheCreek,

Ottima risposta ddopson. Qual è il modo migliore per far comunicare i processi a due nodi sulla stessa macchina? Esiste un protocollo più veloce di TCP quando si trovano sulla stessa macchina?
winduptoy,

1
@Serob_b - beh, sì. L'esecuzione di un'app Node.js su più macchine è molto comune. Non è necessaria alcuna libreria per farlo. Basta eseguire il codice su più macchine e distribuire il carico tra di loro. Progettare il software in modo che si ridimensioni (ovvero memorizza lo stato in una sorta di servizio dati esterno anziché mantenere lo stato in memoria): questo è il tuo lavoro.
Dave Dopson,

45

Un metodo sarebbe quello di eseguire più istanze di node.js sul server e quindi mettere un bilanciamento del carico (preferibilmente uno non bloccante come nginx) davanti a loro.


36
node.js è veloce quanto nginx, potresti mettere un bilanciamento del carico node.js davanti ai tuoi server node.js se lo volessi anche tu :)
mikeal

26
Ryan ha detto specificamente di non farlo fino a quando il nodo non fosse più stabile. Il modo migliore è eseguire nginx davanti al nodo.
rimessa in circolazione il

2
come per nginx davanti al nodo, non risolverà alcuni problemi come se avessi una coda in memoria. Le istanze di 2 nodi non saranno in grado di accedere reciprocamente alla coda.
rimessa in circolazione il

5
Inoltre, nginx non supporta completamente HTTP 1.1, quindi cose come WebSocket non possono essere sottoposte a proxy.
Ashchristopher,

2
@mikeal, resopollution - Sono fortemente dalla parte di Nginx. Node.js è andato in crash più volte (senza stacktrace, muore solo). Non ho mai avuto un incidente con Nginx. Nginx è pronto all'uso con tutti i tipi di acceleratori sani. Node.js per impostazione predefinita continuerà ad accettare nuove connessioni preferendo servire quelle esistenti fino a quando la casella non scende ... sì, l'intera casella; Ho arrestato il kernel su un box CentOS5 eseguendo lo stress test Node (ora QUESTO non dovrebbe succedere davvero). Sono venuto un po 'in giro e vedo un futuro brillante per Node, che include potenzialmente ruoli dedicati di tipo LB. Solo non ancora.
Dave Dopson,

30

Ryan Dahl risponde a questa domanda nel discorso tecnico che ha tenuto a Google l'estate scorsa. Per parafrasare, "basta eseguire processi a più nodi e usare qualcosa di sensato per consentire loro di comunicare, ad es. IPC di tipo sendmsg () o RPC tradizionale".

Se vuoi sporcarti le mani subito, controlla il modulo spark2 Forever . Rende banalmente facili i processi di generazione di più nodi. Gestisce l'impostazione della condivisione delle porte, in modo che ciascuna possa accettare connessioni alla stessa porta e anche la rigenerazione automatica se si desidera assicurarsi che un processo venga riavviato se / quando muore.

AGGIORNAMENTO - 10/11/11 : Il consenso nella comunità dei nodi sembra essere che Cluster sia ora il modulo preferito per la gestione di più istanze di nodo per macchina. Anche per sempre vale la pena dare un'occhiata.


8
Forever e Cluster fanno cose molto diverse. Potresti persino usare entrambi. Per sempre riavvia un processo quando muore. Il cluster gestisce più lavoratori. Utilizzeresti Forever per gestire il tuo processo principale ...
Dave Dopson,

4
inoltre, il modulo learnboost è ampiamente soppiantato dalla versione di Cluster inserita nel Nodo v0.6.x (avviso: la superficie dell'API differisce)
Dave Dopson

@broofa In che modo viene confrontato l'IPC predefinito rispetto all'utilizzo di Redis o Memcache durante l'invio di stringhe / dati / array tra processi? In che modo sarebbe più veloce?
NiCk Newman,

1
@broofa, IPC ha enormi costi generali rispetto alla memoria condivisa reale che Java e C sono in grado di fare.
Pacerier,

@Pacerier Vero, ma la memoria condivisa risolve solo il problema di come ridimensionare nel contesto di un singolo host, senza affrontare i problemi di macro necessari per scalare su molti host. Vale a dire come eseguire nel cloud.
broofa

20

È possibile utilizzare il modulo cluster . Controllare questo .

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);
}

13

Il multi-nodo sfrutta tutti i core che potresti avere.
Dai un'occhiata a http://github.com/kriszyp/multi-node .

Per esigenze più semplici, è possibile avviare più copie del nodo su numeri di porta diversi e mettere un bilanciamento del carico davanti a loro.


12

Node Js supporta il clustering per sfruttare appieno la tua CPU. Se non lo stai eseguendo con il cluster, probabilmente stai sprecando le tue capacità hardware.

Il clustering in Node.js consente di creare processi separati che possono condividere la stessa porta del server. Ad esempio, se eseguiamo un server HTTP su Port 3000, è un server in esecuzione su Single thread su single core del processore.

Il codice mostrato di seguito consente di raggruppare l'applicazione. Questo codice è il codice ufficiale rappresentato da 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.
}

consulta questo articolo per il tutorial completo


11

Come accennato in precedenza, Cluster ridimensionerà e bilancerà il carico della tua app su tutti i core.

aggiungendo qualcosa di simile

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

Riavvia tutti i lavoratori in errore.

Oggigiorno, molte persone preferiscono anche PM2 , che gestisce il clustering per voi e offre anche alcune interessanti funzionalità di monitoraggio .

Quindi, aggiungi Nginx o HAProxy di fronte a diverse macchine in esecuzione con clustering e hai più livelli di failover e una capacità di carico molto più elevata.


3
PM2 è ottimo per l'uso in produzione. Gli strumenti di monitoraggio mi hanno aiutato a risolvere i problemi di memoria con le app.
mbokil,

7

La versione futura del nodo ti consentirà di eseguire il fork di un processo e passare messaggi ad esso e Ryan ha dichiarato di voler trovare un modo per condividere anche i gestori di file, quindi non sarà un'implementazione Web Worker diretta.

Al momento non esiste una soluzione semplice per questo, ma è ancora molto presto e il nodo è uno dei progetti open source più veloci che abbia mai visto, quindi aspettatevi qualcosa di fantastico nel prossimo futuro.


7

Spark2 si basa su Spark che ora non viene più mantenuto. Il cluster è il suo successore e ha alcune caratteristiche interessanti, come la generazione di un processo di lavoro per core della CPU e la rigenerazione di lavoratori morti.


La domanda originale e molte di queste risposte risalgono a qualche mese fa e con il nodo che si muove così velocemente che apprezzo il fatto che tu abbia aggiunto il caos su Cluster. Dopo aver esaminato Cluster e i suoi esempi, sembra esattamente quello che io (o l'OP?) Voglio per Node, grazie!
Riyad Kalla,

5

Sto usando Node worker per eseguire i processi in modo semplice dal mio processo principale. Sembra funzionare alla grande mentre aspettiamo il modo ufficiale di venire in giro.


1
perché node worker example.js non può essere eseguito, il mio nodo è 0.3.3 pre versione
guilin 桂林

5

Il nuovo bambino sul blocco qui è "Up" di LearnBoost .

Fornisce "Ricarichi zero-downtime" e crea inoltre più lavoratori (per impostazione predefinita il numero di CPU, ma è configurabile) per fornire il meglio di tutti i mondi.

È nuovo, ma sembra essere abbastanza stabile, e lo sto usando felicemente in uno dei miei progetti attuali.


5

Il modulo cluster consente di utilizzare tutti i core della macchina. In effetti, puoi trarne vantaggio in soli 2 comandi e senza toccare il tuo codice usando un molto popolare gestore di processi pm2 .

npm i -g pm2
pm2 start app.js -i max

4

È possibile eseguire l'applicazione node.js su più core utilizzando il modulo cluster in combinazione con il modulo os che può essere utilizzato per rilevare quante CPU si dispone.

Ad esempio, immaginiamo di avere un servermodulo che esegue un semplice server http sul back-end e che si desidera eseguirlo per diverse CPU:

// 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();
}


0

È anche possibile progettare il servizio Web come diversi server autonomi che ascoltano socket unix, in modo da poter trasferire funzioni come l'elaborazione dei dati in processi separati.

Ciò è simile alla maggior parte delle architetture di server Web di scrpting / database in cui un processo cgi gestisce la logica aziendale e quindi trasferisce e estrae i dati tramite un socket unix in un database.

la differenza è che l'elaborazione dei dati è scritta come un server web nodo in ascolto su una porta.

è più complesso ma alla fine è dove deve andare lo sviluppo multi-core. un'architettura multiprocesso che utilizza più componenti per ogni richiesta Web.


0

È possibile ridimensionare NodeJS su più box utilizzando un puro bilanciamento del carico TCP (HAProxy) davanti a più box che eseguono un processo NodeJS ciascuno.

Se hai quindi delle conoscenze comuni da condividere tra tutte le istanze, puoi utilizzare un negozio Redis centrale o simile a cui è possibile accedere da tutte le istanze del processo (ad esempio da tutte le caselle)


A meno che tu non abbia CPU single core in quei server, questo non utilizzerà tutta la tua capacità della CPU (a meno che tu non stia facendo anche qualcos'altro).
UpTheCreek,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.