Maison > Article > interface Web > Vous apprendre étape par étape à utiliser les clusters dans Node
Cet article vous permettra d'en savoir plus sur le cluster dans Node.js et de présenter l'événement cluster. J'espère qu'il sera utile à tout le monde !
Node Le module cluster a été directement introduit dans la v0.8 pour résoudre le problème d'utilisation du CPU multicœur. Il fournit également une API relativement complète pour gérer les problèmes de robustesse des processus. Le module
cluster appelle la méthode fork pour créer un processus enfant, qui est la même méthode que fork dans child_process. Le module cluster utilise le modèle maître-esclave classique. Le cluster créera un maître puis copiera plusieurs processus enfants en fonction du nombre que vous spécifiez. Vous pouvez utiliser la propriété cluster.isMaster pour déterminer si le processus actuel est un maître ou un travailleur. (processus de travail). Le processus maître gère tous les processus enfants. Le processus maître n'est pas responsable du traitement de tâches spécifiques. Sa tâche principale est d'être responsable de la planification et de la gestion.
Le module de cluster utilise l'équilibrage de charge intégré pour mieux gérer la pression entre les threads. L'équilibrage de charge utilise l'algorithme Round-robin (également connu sous le nom d'algorithme round-robin). Lors de l'utilisation de la stratégie de planification Round-robin, le maître accepte() toutes les demandes de connexion entrantes, puis envoie le traitement de la demande TCP correspondant au processus de travail sélectionné (cette méthode communique toujours via IPC). L'exemple d'utilisation officiel est le suivant
const cluster = require('cluster'); const cpuNums = require('os').cpus().length; const http = require('http'); if (cluster.isMaster) { for (let i = 0; i < cpuNums; i++){ cluster.fork(); } // 子进程退出监听 cluster.on('exit', (worker,code,signal) => { console.log('worker process died,id',worker.process.pid) }) } else { // 给子进程标注进程名 process.title = `cluster 子进程 ${process.pid}`; // Worker可以共享同一个 TCP 连接,这里是一个 http 服务器 http.createServer((req, res)=> { res.end(`response from worker ${process.pid}`); }).listen(3000); console.log(`Worker ${process.pid} started`); }
En fait, le module cluster est appliqué par une combinaison de modules child_process et net Lorsque le cluster démarre, il démarrera le serveur TCP en interne. Lorsque le processus enfant cluster.fork(), le socket côté serveur TCP Le descripteur de fichier est envoyé au processus de travail. Si le processus de travail est copié via cluster.fork(), alors NODE_UNIQUE_ID existe dans sa variable d'environnement. S'il y a un appel Listen() pour écouter le port réseau dans le processus de travail, il obtiendra le descripteur de fichier via le port SO_REUSEADDR. . Réutiliser, permettant ainsi à plusieurs processus enfants de partager le port.
fork : Cet événement est déclenché après la copie d'un processus de travail
en ligne : Après avoir copié un processus de travail, le processus de travail envoie activement un message en ligne au processus principal, et le processus principal le reçoit Après avoir reçu le message, l'événement est déclenché ;
écoute : après avoir appelé Listen() dans le processus de travail (le Socket côté serveur est partagé), un message d'écoute est envoyé au processus principal. Une fois que le processus principal a reçu le message, l'événement est déclenché ;
disconnect : cet événement sera déclenché lorsque le canal IPC entre le processus principal et le processus de travail est déconnecté ;
exit : cet événement sera déclenché ; lorsqu'un processus de travail se termine ;
setup : exécution de cluster.setupMaster() L'événement est déclenché une fois terminé
La plupart de ces événements sont liés aux événements du module child_process et sont encapsulés en fonction de l'inter- traiter le passage du message.
cluster.on('fork', ()=> { console.log('fork 事件... '); }) cluster.on('online', ()=> { console.log('online 事件... '); }) cluster.on('listening', ()=> { console.log('listening 事件... '); }) cluster.on('disconnect', ()=> { console.log('disconnect 事件... '); }) cluster.on('exit', ()=> { console.log('exit 事件... '); }) cluster.on('setup', ()=> { console.log('setup 事件... '); })
Comme le montre ce qui précède, le processus maître crée des processus de travail via cluster.fork() En fait, cluster.fork() crée en interne des processus enfants via child_process. fourchette(). C'est-à-dire : les processus maître et travailleur sont des processus parent et enfant ; ils communiquent via le canal IPC tout comme les processus parent-enfant créés par child_process.
Le nom complet de l'IPC est Inter-Process Communication, qui est la communication inter-processus. Le but de la communication inter-processus est de permettre à différents processus d'accéder aux ressources et de coordonner leur travail les uns avec les autres. Le canal IPC dans Node est implémenté par la technologie pipe. L'implémentation spécifique est fournie par libuv. Elle est implémentée par un tube nommé sous Windows * nix qui utilise Unix Domain Socket. La communication inter-processus implémentée sur la couche application ne dispose que d'un simple événement de message et d'une méthode d'envoi, très simple à utiliser.
Avant de créer réellement le processus enfant, le processus parent créera un canal IPC et l'écoutera. Ensuite, il créera réellement le processus enfant et indiquera au processus enfant le descripteur de fichier de ce canal IPC via la variable d'environnement (. NODE_CHANNEL_FD). Pendant le processus de démarrage, le processus enfant se connecte au canal IPC existant selon le descripteur de fichier, complétant ainsi la connexion entre les processus parent et enfant.
Une fois la connexion établie, le processus parent-enfant peut communiquer librement. Étant donné que les canaux IPC sont créés à l'aide de canaux nommés ou de sockets de domaine, ils se comportent de la même manière que les sockets réseau et constituent des communications bidirectionnelles. La différence est qu'ils effectuent la communication inter-processus dans le noyau du système sans passer par la couche réseau proprement dite, ce qui est très efficace. Dans Node, le canal IPC est abstrait en tant qu'objet Stream. Lorsque send est appelé, les données sont envoyées (similaire à write ) et le message reçu est déclenché vers la couche application via l'événement de message (similaire à data ).
master 和 worker 进程在 server 实例的创建过程中,是通过 IPC 通道进行通信的,那会不会对我们的开发造成干扰呢?比如,收到一堆其实并不需要关心的消息?答案肯定是不会?那么是怎么做到的呢?
Node 引入进程间发送句柄的功能,send 方法除了能通过 IPC 发送数据外,还能发送句柄,第二个参数为句柄,如下所示
child.send(meeage, [sendHandle])
句柄是一种可以用来标识资源的引用,它的内部包含了指向对象的文件描述符。例如句柄可以用来标识一个服务器端 socket 对象、一个客户端 socket 对象、一个 UDP 套接字、一个管道等。 那么句柄发送跟我们直接将服务器对象发送给子进程有没有什么差别?它是否真的将服务器对象发送给子进程?
其实 send() 方法在将消息发送到 IPC 管道前,将消息组装成两个对象,一个参数是 handle,另一个是 message,message 参数如下所示
{ cmd: 'NODE_HANDLE', type: 'net.Server', msg: message }
发送到 IPC 管道中的实际上是要发送的句柄文件描述符,其为一个整数值。这个 message 对象在写入到 IPC 管道时会通过 JSON.stringify 进行序列化,转化为字符串。子进程通过连接 IPC 通道读取父进程发送来的消息,将字符串通过 JSON.parse 解析还原为对象后,才触发 message 事件将消息体传递给应用层使用。在这个过程中,消息对象还要被进行过滤处理,message.cmd 的值如果以 NODE_ 为前缀,它将响应一个内部事件 internalMessage ,如果 message.cmd 值为 NODE_HANDLE,它将取出 message.type 值和得到的文件描述符一起还原出一个对应的对象。这个过程的示意图如下所示
在 cluster 中,以 worker 进程通知 master 进程创建 server 实例为例子。worker 伪代码如下:
// woker进程 const message = { cmd: 'NODE_CLUSTER', type: 'net.Server', msg: message }; process.send(message);
master 伪代码如下:
worker.process.on('internalMessage', fn);
在前面的例子中,多个 woker 中创建的 server 监听了同个端口 3000,通常来说,多个进程监听同个端口,系统会报 EADDRINUSE 异常。为什么 cluster 没问题呢?
因为独立启动的进程中,TCP 服务器端 socket 套接字的文件描述符并不相同,导致监听到相同的端口时会抛出异常。但对于 send() 发送的句柄还原出来的服务而言,它们的文件描述符是相同的,所以监听相同端口不会引起异常。
这里需要注意的是,多个应用监听相同端口时,文件描述符同一时间只能被某个进程所用,换言之就是网络请求向服务器端发送时,只有一个幸运的进程能够抢到连接,也就是说只有它能为这个请求进行服务,这些进程服务是抢占式的。
pm2 是 node 进程管理工具,可以利用它来简化很多 node 应用管理的繁琐任务,如性能监控、自动重启、负载均衡等。
pm2 自身是基于 cluster 模块进行封装的, 本节我们主要 pm2 的 Satan 进程、God Daemon 守护进程 以及两者之间的进程间远程调用 RPC。
撒旦(Satan),主要指《圣经》中的堕天使(也称堕天使撒旦),被看作与上帝的力量相对的邪恶、黑暗之源,是God 的对立面。
其中 Satan.js 提供程序的退出、杀死等方法,God.js 负责维持进程的正常运行,God 进程启动后一直运行,相当于 cluster 中的 Master进程,维持 worker 进程的正常运行。
RPC(Remote Procedure Call Protocol)是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A 服务器上,想要调用 B 服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。同一机器不同进程间的方法调用也属于 rpc 的作用范畴。 执行流程如下所示
每次命令行的输入都会执行一次 satan 程序,如果 God 进程不在运行,首先需要启动 God 进程。然后根据指令,Satan 通过 rpc 调用 God 中对应的方法执行相应的逻辑。
以 pm2 start app.js -i 4
为例,God 在初次执行时会配置 cluster,同时监听 cluster 中的事件:
// 配置cluster cluster.setupMaster({ exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js') }); // 监听cluster事件 (function initEngine() { cluster.on('online', function(clu) { // worker进程在执行 God.clusters_db[clu.pm_id].status = 'online'; }); // 命令行中 kill pid 会触发exit事件,process.kill不会触发exit cluster.on('exit', function(clu, code, signal) { // 重启进程 如果重启次数过于频繁直接标注为stopped God.clusters_db[clu.pm_id].status = 'starting'; // 逻辑 // ... }); })();
在 God 启动后, 会建立 Satan 和 God 的rpc链接,然后调用 prepare 方法,prepare 方法会调用 cluster.fork 来完成集群的启动
God.prepare = function(opts, cb) { // ... return execute(opts, cb); }; function execute(env, cb) { // ... var clu = cluster.fork(env); // ... God.clusters_db[id] = clu; clu.once('online', function() { God.clusters_db[id].status = 'online'; if (cb) return cb(null, clu); return true; }); return clu; }
本文从 cluster 的基本使用、事件,到 cluster 的基本实现原理,再到 pm2 如何基于 cluster 进行进程管理,带你从入门到深入原理以及了解其高阶应用,希望对你有帮助。
更多node相关知识,请访问:nodejs 教程!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!