Ce que cet article vous apporte, c'est l'analyse (exemples de code) du module child_process et du module cluster dans node.js. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Node suit un modèle de processus unique à thread unique. Le thread unique de node signifie que le moteur js n'a qu'une seule instance et est exécuté dans le thread principal de nodejs. En même temps, le nœud gère les opérations asynchrones telles que. IO de manière événementielle. Le mode de nœud monothread ne maintient qu'un seul thread principal, ce qui réduit considérablement le coût de commutation entre les threads.
Cependant, le thread unique du nœud empêche les opérations gourmandes en CPU d'être effectuées sur le thread principal, sinon le thread principal sera bloqué. Pour les opérations gourmandes en CPU, des processus enfants indépendants peuvent être créés dans le nœud via child_process. Les processus parent et enfant communiquent via IPC. Le processus enfant peut être une application externe ou un sous-programme de nœud. Une fois le processus enfant exécuté, les résultats peuvent être. renvoyé au processus parent.
De plus, le nœud est monothread et s'exécute comme un processus unique, il ne peut donc pas utiliser de processeurs multicœurs et d'autres ressources. Afin de planifier des ressources telles que des processeurs multicœurs, le nœud fournit également un cluster. module pour utiliser les ressources des processeurs multicœurs, permettant de Une série de sous-processus de nœud gérer les tâches de charge tout en assurant un certain équilibrage de charge. Cet article part de la compréhension du thread unique et du processus unique du nœud, et présente le module child_process et le module cluster
Le premier concept à comprendre est que nœud Modes monothread et monoprocessus. Comparé aux modes multithread d'autres langages, le thread unique du nœud réduit le coût de commutation entre les threads, et il n'est pas nécessaire de prendre en compte les problèmes de verrouillage et de pool de threads lors de l'écriture du code du nœud. Le mode monothread déclaré par node est plus adapté aux opérations gourmandes en E/S que les autres langages. Une question classique est donc :
Le nœud est-il vraiment monothread ?
Quand il s'agit de nœud, nous pouvons immédiatement penser à des mots tels que monothread, IO asynchrone et piloté par les événements. La première chose à clarifier est de savoir si le nœud est vraiment monothread. S'il est monothread, alors où les E/S asynchrones et les événements planifiés (setTimeout, setInterval, etc.) sont-ils exécutés.
À proprement parler, le nœud n'est pas monothread. Il existe de nombreux types de threads dans node, notamment :
Thread exécuté par le moteur js
Thread Timer (setTimeout, setInterval)
Thread http asynchrone (ajax)
....
Ce que nous appelons habituellement thread unique signifie qu'il n'y en a qu'un seul dans le moteur node js s'exécute sur le thread principal. D'autres threads d'E/S asynchrones et liés aux événements utilisent libuv pour implémenter des pools de threads internes et la planification des threads. Il existe une boucle d'événement dans libv, et le passage à travers la boucle d'événement peut obtenir un effet similaire au multithreading. En termes simples, Event Loop maintient une pile d'exécution et une file d'attente d'événements si des fonctions d'E/S et de minuterie asynchrones sont trouvées dans la pile d'exécution actuelle, ces fonctions de rappel asynchrones seront placées dans la file d'attente d'événements. Une fois l'exécution de la pile d'exécution actuelle terminée, les fonctions de rappel asynchrones de la file d'attente des événements sont exécutées dans un certain ordre à partir de la file d'attente des événements.
Dans la figure ci-dessus, de la pile d'exécution à la file d'attente des événements, et enfin la fonction de rappel est exécutée dans un certain ordre dans la file d'attente des événements, l'ensemble du processus est une version simplifiée d'Event Loop. De plus, lorsque la fonction de rappel est exécutée, une pile d'exécution sera également générée. Des fonctions asynchrones peuvent être imbriquées dans la fonction de rappel, ce qui signifie que la pile d'exécution est imbriquée.
En d'autres termes, le thread unique dans le nœud signifie que le moteur js ne s'exécute que sur le seul thread principal. D'autres opérations asynchrones ont également des threads indépendants à exécuter. Grâce à la boucle d'événements de libv, un multi-thread similaire est implémenté. . Changement de contexte de thread et planification de pool de threads. Les threads sont les plus petits processus, donc le nœud est également un processus unique. Cela explique pourquoi le nœud est monothread et monoprocessus.
Puisque le nœud est un processus unique, il doit y avoir un problème, c'est-à-dire qu'il ne peut pas utiliser pleinement les ressources telles que le processeur. Node fournit le module child_process pour implémenter les processus enfants, réalisant ainsi un modèle multi-processus au sens large. Grâce au module child_process, le mode d'un processus principal et de plusieurs sous-processus peut être réalisé. Le processus principal est appelé processus maître et le sous-processus est également appelé processus de travail. Dans le sous-processus, vous pouvez non seulement appeler d'autres programmes de nœud, mais également exécuter des programmes sans nœud et des commandes shell, etc. Après avoir exécuté le sous-processus, il revient sous la forme d'un flux ou d'un rappel.
child_process fournit 4 méthodes pour créer de nouveaux processus enfants. Ces 4 méthodes sont spawn, execFile, exec et fork. Toutes les méthodes sont asynchrones et une image peut être utilisée pour décrire les différences entre ces quatre méthodes.
L'image ci-dessus peut montrer les différences entre ces quatre méthodes. Nous pouvons également présenter brièvement les différences entre ces quatre méthodes.
spawn : Un programme sans nœud est exécuté dans le processus enfant. Une fois qu'un ensemble de paramètres est fourni, le résultat de l'exécution est renvoyé sous la forme d'un flux.
execFile : Un programme sans nœud est exécuté dans le processus enfant. Une fois qu'un ensemble de paramètres est fourni, le résultat de l'exécution est renvoyé sous la forme d'un. rappel.
exec : le processus enfant exécute un programme non-nœud, en passant une chaîne de commandes shell, et le résultat est renvoyé sous la forme d'un rappel après exécution, ce qui est différent de execFile
Le fait est que exec peut exécuter directement une série de commandes shell.
fork : Le processus enfant exécute le programme de nœud après avoir fourni un ensemble de paramètres, le résultat de l'exécution est renvoyé sous la forme d'un flux. Contrairement à spawn, fork génère Les processus enfants ne peuvent exécuter que des applications de nœud. Les sections suivantes présenteront ces méthodes en détail.
Nous comparons d'abord la différence entre execFile et exec Les similitudes entre ces deux méthodes :
Une application non-nœud est exécutée et le résultat de l'exécution est renvoyé sous la forme d'une fonction de rappel.
La différence est la suivante :
exec est une commande shell qui est directement exécutée, tandis que execFile est une application qui est exécutée
Par exemple, echo est une commande intégrée du système UNIX Nous pouvons l'exécuter directement sur la ligne de commande :
echo hello world
En conséquence, hello world sera. imprimé sur la ligne de commande.
Créez un nouveau fichier main.js Si vous souhaitez utiliser la méthode exec, écrivez dans le fichier :
let cp=require('child_process'); cp.exec('echo hello world',function(err,stdout){ console.log(stdout); });
L'exécution de ce main.js affichera hello world. Nous avons constaté que le premier paramètre de exec est complètement similaire à la commande shell.
let cp=require('child_process'); cp.execFile('echo',['hello','world'],function(err,stdout){ console.log(stdout); });
execFile est similaire à l'exécution d'une application nommée echo puis à la transmission de paramètres. execFlie recherchera une application nommée « echo » dans le chemin de process.env.PATH et l'exécutera après l'avoir trouvée. Le chemin process.env.PATH par défaut contient 'usr/local/bin', et ce programme nommé 'echo' existe dans le répertoire 'usr/local/bin', en passant les deux paramètres hello et world , revient après l'exécution.
Comme exec, il est extrêmement dangereux d'exécuter directement un shell. Par exemple, il existe un tel shell :
echo hello world;rm -rf
Par exec. Il peut être exécuté directement. rm -rf supprimera les fichiers du répertoire courant. exec est comme la ligne de commande, le niveau d'exécution est très élevé et des problèmes de sécurité surviendront après l'exécution, mais execFile est différent :
execFile('echo',['hello','world',';rm -rf'])
Lors du passage des paramètres, la sécurité de l'exécution du passé les paramètres réels seront détectés. S'il y a un problème de sécurité, une exception sera levée. En plus de execFile, spawn et fork ne peuvent pas exécuter directement le shell, ils sont donc plus sécurisés.
spawn est également utilisé pour exécuter des applications sans nœud et ne peut pas exécuter directement le shell. Par rapport à execFile, le résultat de l'exécution du spawn de l'application n'est pas unique. résultat une fois l’exécution terminée. La sortie se présente sous la forme d’un flux. Pour les gros lots de données en sortie, l'utilisation de la mémoire peut être introduite sous forme de flux.
Prenons l'exemple du tri et de la déduplication d'un fichier :
Dans le diagramme d'image ci-dessus, l'entrée est lue d'abord. Il y a du texte acba non trié dans le fichier txt. La fonction de tri peut être implémentée via le programme de tri, et la sortie est aabc. Enfin, le programme uniq peut être utilisé pour supprimer les doublons et obtenir abc. Nous pouvons utiliser l'entrée et la sortie du flux de génération pour implémenter les fonctions ci-dessus :
let cp=require('child_process'); let cat=cp.spawn('cat',['input.txt']); let sort=cp.spawn('sort'); let uniq=cp.spawn('uniq'); cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout); console.log(process.stdout);
Après l'exécution, le résultat final sera saisi dans process.stdout. Si le fichier input.txt est volumineux, les entrées et sorties sous forme de flux peuvent réduire considérablement l'utilisation de la mémoire. En définissant un tampon, l'utilisation de la mémoire peut être réduite tout en améliorant l'efficacité des entrées et des sorties.
En JavaScript, en termes de traitement d'un grand nombre de tâches de calcul, HTML est implémenté via le travail Web, ce qui sépare la tâche du fil principal. Une communication intégrée entre le processus parent et le processus enfant est utilisée dans le nœud pour gérer ce problème, réduisant ainsi la pression sur les opérations Big Data. La méthode fork est fournie dans le nœud.Grâce à la méthode fork, le programme de nœud est exécuté dans un processus séparé et, grâce à la communication entre le père et le fils, le processus enfant accepte les informations du processus parent et renvoie les résultats de l'exécution au processus parent. .
En utilisant la méthode fork, un canal IPC peut être ouvert entre le processus parent et le processus enfant, permettant la communication de messages entre différents processus de nœuds.
Dans le processus enfant :
Recevoir et envoyer des messages via les mécanismes de process.on('message') et process.send().
Dans le processus parent :
Recevoir et envoyer via les mécanismes de child.on('message') et process.send() information.
Exemple spécifique, dans child.js :
process.on('message',function(msg){ process.send(msg) })
Dans parent.js :
let cp=require('child_process'); let child=cp.fork('./child'); child.on('message',function(msg){ console.log('got a message is',msg); }); child.send('hello world');
L'exécution de parent.js affichera le résultat sur la ligne de commande : j'ai reçu un message, c'est bonjour tout le monde
Pour interrompre la communication entre père et fils, vous pouvez déconnecter la communication IPC entre père et fils en appelant :
child.disconnect()
dans le parent processus.
exec、execFile、spawn和fork执行的子进程都是默认异步的,子进程的运行不会阻塞主进程。除此之外,child_process模块同样也提供了execFileSync、spawnSync和execSync来实现同步的方式执行子进程。
cluster意为集成,集成了两个方面,第一个方面就是集成了child_process.fork方法创建node子进程的方式,第二个方面就是集成了根据多核CPU创建子进程后,自动控制负载均衡的方式。
我们从官网的例子来看:
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`主进程 ${process.pid} 正在运行`); // 衍生工作进程。 for (let i = 0; i { console.log(`工作进程 ${worker.process.pid} 已退出`); }); } else { // 工作进程可以共享任何 TCP 连接。 // 在本例子中,共享的是一个 HTTP 服务器。 http.createServer((req, res) => { res.writeHead(200); res.end('你好世界\n'); }).listen(8000); console.log(`工作进程 ${process.pid} 已启动`); }
最后输出的结果为:
$ node server.js 主进程 3596 正在运行 工作进程 4324 已启动 工作进程 4520 已启动 工作进程 6056 已启动 工作进程 5644 已启动
我们将master称为主进程,而worker进程称为工作进程,利用cluster模块,使用node封装好的API、IPC通道和调度机可以非常简单的创建包括一个master进程下HTTP代理服务器 + 多个worker进程多个HTTP应用服务器的架构。
本文首先介绍了node的单线程和单进程模式,接着从单线程的缺陷触发,介绍了node中如何实现子进程的方法,对比了child_process模块中几种不同的子进程生成方案,最后简单介绍了内置的可以实现子进程以及CPU进程负载均衡的内置集成模块cluster。
相关推荐:
Node.js中关于多进程模块Cluster的详细介绍以及如何使用
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!