[ Ce billet est à jour en date du 2012-09-02 (plus récent que ci-dessus). ]
Node.js s'adapte parfaitement aux machines multi-cœurs.
Oui, Node.js est un thread par processus. Il s'agit d'une décision de conception très délibérée qui élimine le besoin de gérer la sémantique du verrouillage. Si vous n'êtes pas d'accord avec cela, vous ne réalisez probablement pas encore à quel point il est difficile de déboguer un code multithread. Pour une explication plus approfondie du modèle de processus de Node.js et de la raison pour laquelle il fonctionne de cette façon (et pourquoi il ne supportera JAMAIS les threads multiples), lisez le document suivant mon autre poste .
Alors, comment puis-je tirer parti de ma boîte à 16 noyaux ?
De deux façons :
- Pour les grosses tâches de calcul lourd comme le codage d'images, Node.js peut lancer des processus enfants ou envoyer des messages à des processus travailleurs 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 dévorant les 15 autres CPU.
- Pour mettre à l'échelle le débit d'un service Web, vous devriez exécuter plusieurs serveurs Node.js sur une boîte, un par cœur, et répartir le trafic des demandes entre eux. Cela permet d'obtenir une excellente affinité avec le CPU et de faire évoluer le débit de façon presque linéaire avec le nombre de cœurs.
Mise à l'échelle du débit d'un service web
Depuis la version 6.0.X, Node.js a inclus le module cluster ce qui facilite la mise en place de plusieurs nœuds travailleurs qui peuvent écouter sur un seul port. Notez que ce n'est PAS la même chose que l'ancien module "cluster" de learnboost disponible par le biais de 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é a le plus de chances de l'emporter. Cela fonctionne plutôt bien et peut augmenter le débit sur une machine multi-core.
Si vous avez une charge suffisante pour vous préoccuper de plusieurs cœurs, vous allez vouloir faire un peu plus de choses aussi :
-
Exécutez votre service Node.js derrière un proxy web tel que Nginx ou Apache - quelque chose qui peut limiter les connexions (à moins que vous ne souhaitiez que les conditions de surcharge fassent complètement tomber la boîte), réécrire les URL, servir du contenu statique et mettre en place d'autres sous-services.
-
Recyclez périodiquement vos processus de travail. Pour un processus qui tourne depuis longtemps, même une petite fuite de mémoire finit par s'accumuler.
-
Configuration de la collecte et de la surveillance des journaux
PS : Il y a une discussion entre Aaron et Christopher dans les commentaires d'un autre article (à l'heure où j'écris ces lignes, c'est l'article le plus important). Quelques commentaires à ce sujet :
- Un modèle de socket partagé est très pratique pour permettre à plusieurs processus d'écouter sur un même port et de se faire concurrence pour accepter de nouvelles connexions. D'un point de vue conceptuel, on pourrait penser qu'Apache préfourché fait cela, avec la réserve importante que chaque processus n'acceptera qu'une seule connexion et mourra ensuite. La perte d'efficacité d'Apache se situe au niveau des frais généraux liés à la création de nouveaux processus et n'a rien à voir avec les opérations sur les sockets.
- Pour Node.js, avoir N travailleurs en concurrence sur un seul socket est une solution extrêmement raisonnable. L'autre solution consiste à mettre en place un front-end intégré comme Nginx et à faire en sorte que le trafic soit acheminé par proxy vers les différents workers, en alternant entre les workers pour l'attribution de nouvelles connexions. Ces deux solutions présentent des caractéristiques de performance très similaires. Et puisque, comme je l'ai mentionné ci-dessus, vous voudrez probablement avoir Nginx (ou une alternative) en tête de votre service de nœuds 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)
vs
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 quelques avantages à la configuration des ports individuels (possibilité d'avoir moins de couplage entre les processus, d'avoir des décisions d'équilibrage de charge plus sophistiquées, etc.), mais c'est définitivement plus de travail à mettre en place et le module cluster intégré est une alternative peu complexe qui fonctionne pour la plupart des gens.
4 votes
Il semble que Ryah commence à prendre au sérieux l'inclusion d'un support multi-core intégré dans node : github.com/joyent/node/commit/
3 votes
Le gestionnaire de processus PM2 utilise le module cluster en interne pour répartir vos applications NodeJS sur tous les cœurs disponibles : github.com/Unitech/pm2
1 votes
@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 pour Nodejs du threading réel et des variables volatiles-statiques de Java ? .