Node.js sur les machines multicœurs

606

Node.js semble intéressant, MAIS je dois manquer quelque chose - Node.js n'est-il pas réglé uniquement pour fonctionner sur un seul processus et thread?

Alors, comment évolue-t-il pour les processeurs multicœurs et les serveurs multi-processeurs? Après tout, c'est génial de faire le plus rapidement possible un serveur monofil, mais pour des charges élevées, je voudrais utiliser plusieurs processeurs. Et il en va de même pour rendre les applications plus rapides - il semble aujourd'hui que la méthode consiste à utiliser plusieurs processeurs et à paralléliser les tâches.

Comment Node.js s'intègre-t-il dans cette image? Est-ce son idée de distribuer en quelque sorte plusieurs instances ou quoi?

zaharpopov
la source
4
Il semble que Ryah commence à sérieusement inclure le support multicœur intégré dans le nœud: github.com/joyent/node/commit/…
broofa
2
Le gestionnaire de processus PM2 utilise le module de cluster en interne pour diffuser vos applications NodeJS sur tous les cœurs disponibles: github.com/Unitech/pm2
Unitech
@broofa, Ce ne sont pas de vrais threads et les processus enfants n'ont pas de mémoire partagée. Voir aussi Quel est l'équivalent Nodejs du vrai threading et des variables volatiles-statiques de Java? .
Pacerier

Réponses:

697

[ Ce message est à jour en date du 2012-09-02 (plus récent que ci-dessus). ]

Node.js évolue absolument sur les machines multicœurs.

Oui, Node.js est un thread par processus. Il s'agit d'une décision de conception très délibérée et élimine la nécessité de gérer la sémantique de verrouillage. Si vous n'êtes pas d'accord avec cela, vous ne réalisez probablement pas encore à quel point il est incroyablement difficile de déboguer du code multithread. Pour une explication plus approfondie du modèle de processus Node.js et pourquoi il fonctionne de cette façon (et pourquoi il ne prendra JAMAIS en charge plusieurs threads), lisez mon autre article .

Alors, comment puis-je profiter de ma boîte à 16 cœurs?

Deux façons:

  • Pour les tâches de calcul lourdes comme le codage d'image, Node.js peut lancer des processus enfants ou envoyer des messages à des processus de travail supplémentaires. Dans cette conception, vous auriez un thread gérant le flux d'événements et N processus effectuant des tâches de calcul lourdes et mâchant les 15 autres CPU.
  • Pour faire évoluer le débit sur un service Web, vous devez exécuter plusieurs serveurs Node.js sur une seule boîte, un par cœur et répartir le trafic de demande entre eux. Cela offre une excellente affinité CPU et augmentera le débit de manière presque linéaire avec le nombre de cœurs.

Mise à l'échelle du débit sur un service Web

Depuis v6.0.X Node.js a inclus le module de cluster directement, ce qui facilite la configuration de plusieurs nœuds de travail pouvant écouter sur un seul port. Notez que ce n'est PAS le même que l'ancien module "cluster" learnboost disponible via npm .

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

Les travailleurs seront en concurrence pour accepter de nouvelles connexions, et le processus le moins chargé est le plus susceptible de gagner. Cela fonctionne assez bien et peut augmenter le débit assez bien sur une boîte multicœur.

Si vous avez suffisamment de charge pour vous soucier de plusieurs cœurs, vous voudrez également faire quelques choses:

  1. Exécutez votre service Node.js derrière un proxy Web comme Nginx ou Apache - quelque chose qui peut limiter la connexion (sauf si vous voulez que les conditions de surcharge réduisent complètement la boîte), réécrire les URL, servir du contenu statique et proxy d'autres sous-services.

  2. Recyclez régulièrement vos processus de travail. Pour un processus de longue durée, même une petite fuite de mémoire finira par s'additionner.

  3. Configuration de la collecte / surveillance des journaux


PS: Il y a une discussion entre Aaron et Christopher dans les commentaires d'un autre article (à ce jour, c'est le premier article). Quelques commentaires à ce sujet:

  • Un modèle de socket partagé est très pratique pour permettre à plusieurs processus d'écouter sur un seul port et de concurrencer pour accepter de nouvelles connexions. Sur le plan conceptuel, vous pourriez penser à Apache préfork faisant cela avec la mise en garde importante que chaque processus n'acceptera qu'une seule connexion, puis mourra. La perte d'efficacité pour Apache est dans la surcharge de forking de nouveaux processus et n'a rien à voir avec les opérations de socket.
  • Pour Node.js, avoir N travailleurs en concurrence sur un seul socket est une solution extrêmement raisonnable. L'alternative consiste à configurer un frontal sur boîte comme Nginx et à disposer de ce trafic proxy vers les différents travailleurs, en alternant entre les travailleurs pour attribuer de nouvelles connexions. Les deux solutions ont des caractéristiques de performances très similaires. Et comme, comme je l'ai mentionné ci-dessus, vous voudrez probablement que Nginx (ou une alternative) gère votre service de nœud de toute façon, le choix ici est vraiment entre:

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

contre

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

Il y a sans doute certains avantages à la configuration des ports individuels (potentiel d'avoir moins de couplage entre les processus, d'avoir des décisions d'équilibrage de charge plus sophistiquées, etc.), mais c'est certainement plus de travail à configurer et le module de cluster intégré est un faible -alternative de complexité qui fonctionne pour la plupart des gens.

Dave Dopson
la source
1
pouvez-vous offrir des conseils pour exécuter différents services basés sur nodejs sur une seule boîte? Par exemple, disons que j'ai 1 serveur et que je veux exécuter myservice1.js sur CpuCore1 et myservice2.js sur CpuCore2. Puis-je utiliser le cluster pour cela? ou est-il uniquement utile pour créer des services clonés?
UpTheCreek
6
Tu devrais poster une question pour ça! (et je vais copier ce commentaire comme première réponse). Ce que vous voulez faire est vraiment très simple. Vous n'auriez pas vraiment besoin de "cluster", vous exécuteriez simplement deux services de nœuds différents. Deux scripts, deux processus, deux ports. Par exemple, vous pouvez avoir serviceA écouter sur 3000 et serviceB écouter sur 3001. Chacun de ces services peut utiliser "cluster" pour avoir 1+ travailleurs et les recycler périodiquement, etc. Ensuite, vous pouvez configurer Nginx pour écouter sur le port 80 et pour transférer vers le service correct basé sur l'en-tête "Host" entrant et / ou le chemin URL.
Dave Dopson
1
Merci. J'ai déjà posté une question connexe - vous avez décrit à peu près ce que j'avais à l'esprit, mais je ne sais pas comment cibler les cœurs de processeur (lors de l'utilisation de quelque chose comme pour toujours).
UpTheCreek
Grande réponse ddopson. Quelle est la meilleure façon de faire communiquer entre eux deux processus de noeud sur la même machine? Existe-t-il un protocole plus rapide que TCP lorsqu'ils sont sur la même machine?
winduptoy
1
@Serob_b - eh bien, oui. L'exécution d'une application Node.js sur plusieurs machines est très courante. Aucune bibliothèque n'est nécessaire pour le faire. Vous exécutez simplement votre code sur plusieurs machines et répartissez la charge entre elles. L'architecture de votre logiciel pour qu'il évolue (c'est-à-dire qu'il stocke l'état dans une sorte de service de données externe plutôt que de garder l'état en mémoire) - c'est votre travail.
Dave Dopson
45

Une méthode consisterait à exécuter plusieurs instances de node.js sur le serveur, puis à placer un équilibreur de charge (de préférence un non bloquant comme nginx) devant eux.

Chandra Sekar
la source
36
node.js est à peu près aussi rapide que nginx, vous pouvez mettre un équilibreur de charge node.js devant vos serveurs node.js si vous le souhaitez aussi :)
mikeal
26
ryan a spécifiquement dit de ne pas le faire avant que le nœud ne soit plus stable. Le meilleur moyen est d'exécuter nginx devant le nœud.
resopollution
2
quant à nginx devant le noeud, il ne résoudra pas certains problèmes comme si vous avez une file d'attente en mémoire. 2 instances de nœuds ne pourront pas accéder à la file d'attente de l'autre.
resopollution
5
De plus, nginx ne prend pas complètement en charge HTTP 1.1, donc des choses comme WebSockets ne peuvent pas être mandatées.
ashchristopher
2
@mikeal, resopollution - Je suis fortement du côté de Nginx. J'ai crashé dur Node.js plusieurs fois (pas de trace de pile, meurt juste). Je n'ai jamais écrasé Nginx. Nginx prêt à l'emploi est configuré avec toutes sortes d'accélérateurs sains. Node.js par défaut continuera à accepter de nouvelles connexions de préférence à servir les connexions existantes jusqu'à ce que la boîte tombe en panne ... oui, la boîte entière; J'ai planté le noyau sur une boîte CentOS5 en testant le stress du nœud (maintenant cela n'est vraiment pas censé se produire). Je suis venu un peu et je vois un brillant avenir pour Node, y compris potentiellement des rôles dédiés de type LB. Mais pas encore.
Dave Dopson
30

Ryan Dahl répond à cette question dans le discours technique qu'il a donné à Google l'été dernier. Pour paraphraser, "exécutez simplement plusieurs processus de nœuds et utilisez quelque chose de sensé pour leur permettre de communiquer. Par exemple IPC de style sendmsg () ou RPC traditionnel".

Si vous voulez vous salir les mains immédiatement, consultez le module spark2 Forever . Il facilite la génération de plusieurs processus de nœuds. Il gère la configuration du partage de port, afin qu'ils puissent chacun accepter des connexions au même port, ainsi que la réapparition automatique si vous voulez vous assurer qu'un processus est redémarré si / quand il meurt.

MISE À JOUR - 11/11/11 : Le consensus dans la communauté des nœuds semble être que le cluster est maintenant le module préféré pour gérer plusieurs instances de nœuds par machine. Forever vaut également le détour.

broofa
la source
8
Forever et Cluster font des choses très différentes. Vous pourriez même utiliser les deux. Forever redémarre un processus lorsqu'il meurt. Le cluster gère plusieurs employés. Vous utiliseriez Forever pour gérer votre processus maître ...
Dave Dopson
4
en outre, le module learnboost est largement supplanté par la version de Cluster intégrée au nœud v0.6.x (avertissement: la surface de l'API diffère)
Dave Dopson
@broofa Comment l'IPC par défaut est-il comparé à, disons, en utilisant Redis ou Memcache wile en envoyant simplement des chaînes / données / tableaux entre les processus? Quelle voie serait plus rapide?
NiCk Newman
1
@broofa, IPC a d'énormes frais généraux par rapport à la mémoire partagée réelle que Java et C sont capables de faire.
Pacerier
@Pacerier Vrai, mais la mémoire partagée ne résout que le problème de la mise à l'échelle dans le contexte d'un seul hôte, sans résoudre les problèmes de macro nécessaires à la mise à l'échelle sur de nombreux hôtes. C'est à dire comment courir dans le Cloud.
broofa
20

Vous pouvez utiliser le module de cluster . Vérifiez ça .

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
la source
13

Plusieurs nœuds exploitent tous les cœurs que vous pouvez avoir.
Jetez un œil à http://github.com/kriszyp/multi-node .

Pour des besoins plus simples, vous pouvez démarrer plusieurs copies de nœud sur différents numéros de port et placer un équilibreur de charge devant eux.

CyberFonic
la source
12

Node Js prend en charge le clustering pour tirer pleinement parti de votre processeur. Si vous ne l'exécutez pas avec le cluster, vous perdez probablement vos capacités matérielles.

Le clustering dans Node.js vous permet de créer des processus distincts qui peuvent partager le même port de serveur. Par exemple, si nous exécutons un serveur HTTP sur le port 3000, il s'agit d'un serveur fonctionnant sur un seul thread sur un seul cœur de processeur.

Le code ci-dessous vous permet de regrouper votre application. Ce code est le code officiel représenté par 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.
}

consultez cet article pour le tutoriel complet

Toumi
la source
11

Comme mentionné ci-dessus, Cluster dimensionnera et équilibrera la charge de votre application sur tous les cœurs.

ajouter quelque chose comme

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

Redémarrera tous les travailleurs défaillants.

De nos jours, beaucoup de gens préfèrent également PM2 , qui gère le clustering pour vous et fournit également des fonctionnalités de surveillance intéressantes .

Ensuite, ajoutez Nginx ou HAProxy devant plusieurs machines fonctionnant avec le clustering et vous avez plusieurs niveaux de basculement et une capacité de charge beaucoup plus élevée.

Will Stern
la source
3
Le PM2 est idéal pour une utilisation en production. Les outils de surveillance m'ont aidé à résoudre les problèmes de mémoire avec les applications.
mbokil
7

La future version du nœud vous permettra de bifurquer un processus et de lui passer des messages et Ryan a déclaré qu'il voulait trouver un moyen de partager également les gestionnaires de fichiers, donc ce ne sera pas une implémentation Web Worker simple.

Pour le moment, il n'y a pas de solution facile pour cela, mais c'est encore très tôt et le nœud est l'un des projets open source les plus rapides que j'ai jamais vus, alors attendez-vous à quelque chose de génial dans un avenir proche.

mikeal
la source
7

Spark2 est basé sur Spark qui n'est désormais plus maintenu. Cluster est son successeur, et il a des fonctionnalités intéressantes, comme la génération d'un processus de travail par cœur de processeur et la réapparition de travailleurs morts.

Le développeur
la source
La question d'origine et beaucoup de ces réponses datent de quelques mois et avec le nœud se déplaçant si rapidement, j'apprécie que vous ajoutiez le texte de présentation sur Cluster. Après avoir regardé Cluster et ses exemples, il ressemble exactement à ce que je (ou l'OP?) Veux pour Node, merci!
Riyad Kalla
5

J'utilise Node Worker pour exécuter des processus de manière simple à partir de mon processus principal. Semble fonctionner très bien pendant que nous attendons la manière officielle de contourner.

christkv
la source
1
pourquoi le nœud de travail example.js ne peut pas fonctionner, mon nœud est la pré-version 0.3.3
guilin
5

Le nouveau sur le bloc ici est "Up" de LearnBoost .

Il fournit des «rechargements sans temps d'arrêt» et crée en outre plusieurs travailleurs (par défaut, le nombre de CPU, mais il est configurable) pour fournir le meilleur de tous les mondes.

Il est nouveau, mais semble assez stable, et je l'utilise avec bonheur dans l'un de mes projets en cours.

Roy
la source
5

Le module de cluster vous permet d'utiliser tous les cœurs de votre machine. En fait, vous pouvez en profiter en seulement 2 commandes et sans toucher à votre code en utilisant un gestionnaire de processus très populaire pm2 .

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

Vous pouvez exécuter votre application node.js sur plusieurs cœurs en utilisant le module de cluster en combinaison avec le module os qui peut être utilisé pour détecter le nombre de CPU dont vous disposez.

Par exemple, imaginons que vous avez un servermodule qui exécute un serveur http simple sur le backend et que vous souhaitez l'exécuter pour plusieurs processeurs:

// 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
la source
0

Il est également possible de concevoir le service Web comme plusieurs serveurs autonomes qui écoutent les sockets Unix, de sorte que vous pouvez pousser des fonctions telles que le traitement des données dans des processus séparés.

Ceci est similaire à la plupart des architectures de serveur Web scrpting / base de données où un processus cgi gère la logique métier, puis pousse et extrait les données via un socket Unix vers une base de données.

la différence étant que le traitement des données est écrit comme un serveur web de noeud écoutant sur un port.

c'est plus complexe, mais en fin de compte, c'est là où le développement multicœur doit aller. une architecture multiprocessus utilisant plusieurs composants pour chaque requête Web.

Corbeau de feu
la source
0

Il est possible de faire évoluer NodeJS sur plusieurs boîtiers à l'aide d'un équilibreur de charge TCP pur (HAProxy) devant plusieurs boîtiers exécutant chacun un processus NodeJS.

Si vous avez alors des connaissances communes à partager entre toutes les instances, vous pouvez utiliser un magasin Redis central ou similaire auquel vous pouvez ensuite accéder à partir de toutes les instances de processus (par exemple à partir de toutes les boîtes)

Martin Tajur
la source
À moins que vous n'ayez des processeurs monocœur sur ces serveurs, cela n'utilisera pas toute la capacité de votre processeur (à moins que vous ne fassiez autre chose également).
UpTheCreek