Les amis qui connaissent js savent que js 是单线程
Node, un modèle multiprocessus monothread est adopté. En raison de la limitation du JavaScript à un seul thread, sur les serveurs multicœurs, nous devons souvent démarrer plusieurs processus pour maximiser les performances du serveur.
Les clusters de processus Node.js peuvent être utilisés pour exécuter plusieurs instances Node.js, qui peuvent répartir la charge de travail entre leurs threads d'application. Lorsque l'isolation des processus n'est pas requise, utilisez plutôt le module worker_threads
, qui permet d'exécuter plusieurs threads d'application au sein d'une seule instance Node.js.
Node a introduit le module cluster après la version V0.8,一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
Le module cluster facilite la création de processus enfants partageant les ports du serveur.
La couche inférieure du cluster est le module child_process. En plus d'envoyer des messages ordinaires, il peut également envoyer des objets sous-jacents
TCP
,UDP
, etc.cluster
est une application combinée du modulechild_process
et du modulenet
. Lorsque le cluster démarre, le serveur TCP sera démarré en interne et le descripteur de fichier du socket du serveur TCP sera envoyé au processus de travail.
Dans l'application du module cluster
,一个主进程只能管理一组工作进程
. Son mode de fonctionnement n'est pas aussi flexible que le module child_process
, mais il est plus stable :
const cluster = require('cluster') et complexe
.isMaster
identifie le processus principal, Node<16.isPrimary.isPrimary
le processus principal, Node>16.isWorker.isWorker
le sous - processus. .worker est.worker
du travail en cours. Référence à l'objet de processus [dans le processus enfant].workers
stocke le hachage de l'objet de processus de travail actif, avec le champ id
comme clé. Cela facilite la navigation dans tous les processus de travail. Il n'est disponible que dans le processus principal. cluster.wokers[id] === worker
[dans le processus principal].settings
est en lecture seule, élément de configuration du cluster. Après avoir appelé la méthode .setupPrimary() ou .fork(), cet objet de paramètres contiendra les paramètres, y compris les valeurs par défaut. Auparavant, un objet vide. Cet objet ne doit pas être modifié ou défini manuellement.cluster.settings
:- `execArgv` <string[]>Une liste de paramètres de chaîne transmise au fichier exécutable Node.js. **Par défaut :** `process.execArgv`. - `exec` <string> Chemin du fichier vers le fichier du processus de travail. **Par défaut :** `process.argv[1]`. - `args` <string[]> Arguments de chaîne transmis au processus de travail. **Par défaut :** `process.argv.slice(2)`. - `cwd` <string>Le répertoire de travail actuel du processus de travail. **Par défaut :** « non défini » (hérité du processus parent). - `serialization` <string>Spécifie le type de sérialisation utilisé pour envoyer des messages entre les processus. Les valeurs possibles sont « json » et « avancé ». **Par défaut :** `false`. - `silent` <boolean>S'il faut envoyer la sortie à l'entrée et à la sortie standard du processus parent. **Par défaut :** `false`. - `stdio` <Array> configure l'entrée et la sortie standard du processus généré. Étant donné que le module cluster s'appuie sur IPC pour s'exécuter, cette configuration doit contenir une entrée « ipc ». Lorsque cette option est fournie, elle remplace « silencieux ». - `uid` <numéro> définit l'ID utilisateur du processus. - `gid` <numéro> définit l'ID de groupe du processus. - `inspectPort` <number> | <Function> Définit le port de l'inspecteur pour le processus de travail. Il peut s'agir d'un nombre ou d'une fonction qui ne prend aucun paramètre et renvoie un nombre. Par défaut, chaque processus de travail a son propre port, en commençant par le « process.debugPort » du processus principal et en augmentant. - `windowsHide` <boolean> Masquer la fenêtre de la console de processus générée normalement créée sur les systèmes Windows. **Par défaut :** `false`.
.fork([env])
génère un nouveau processus de travail [dans le processus principal].setupPrimary([settings])
Node>16.setupMaster([settings])
est utilisé pour modifier le comportement 'fork' par défaut, après utilisation Les paramètres apparaîtront dans cluster.settings
. Toute modification de paramètre n'affectera que les futurs appels à .fork()
, et non les processus de travail en cours d'exécution. Les valeurs par défaut ci-dessus s'appliquent uniquement au premier appel. Le nœud est inférieur à 16 [Dans le processus principal].disconnect([callback])
Appelé lorsque tous les processus de travail se déconnectent et ferment les poignées [Dans le processus principal]Afin de rendre le cluster plus stable et robuste, cluster
également expose de nombreux événements Event :
'message'
, déclenché lorsque le processus maître du cluster reçoit un message d'un processus de travail.'exit'
, lorsqu'un processus de travail meurt, le module du cluster déclenchera l'événement 'exit'
.cluster.on('exit', (travailleur, code, signal) => { console.log('le travailleur %d est décédé (%s). redémarrage...', travailleur.process.pid, signal ||); cluster.fork(); });
'listening'
, après avoir appelé listen()
depuis le processus de travail, lorsque l'événement 'listening'
est déclenché sur le serveur, cluster
dans le processus principal déclenchera également l'événement 'listening'
.cluster.on('écoute', (travailleur, adresse) => { console.log( `Un travailleur est maintenant connecté à ${address.address}:${address.port}`); });
'fork'
, lorsqu'un nouveau processus de travail est généré, le module de cluster déclenchera l'événement 'fork'
.cluster.on('fork', (travailleur) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
, déclenché à chaque appel .setupPrimary()
.disconnect
est déclenché après la déconnexion du canal IPC du processus de travail. Lorsque le processus de travail se termine normalement, est arrêté ou se déconnecte manuellementcluster.on('disconnect', (worker) => { console.log(`Le travailleur #${worker.id} s'est déconnecté`); });
L'objet Worker
contient toutes les informations publiques et les méthodes du processus de travail. Dans le processus principal, vous pouvez utiliser cluster.workers
pour l'obtenir. Dans un processus de travail, vous pouvez utiliser cluster.worker
pour l'obtenir.
.id
. Chaque nouveau processus de travail reçoit son propre identifiant unique. Cet identifiant est stocké dans id
. Lorsqu'un processus de travail est actif, c'est la clé qui l'indexe dans cluster.workers
..process
Tous les processus de travail sont créés à l'aide de child_process.fork()
, et l'objet renvoyé par cette fonction est stocké sous .process
. Dans le processus de travail, le process
global est stocké..send(message[, sendHandle[, options]][, callback])
envoie un message au processus de travail ou au processus principal, et vous pouvez choisir d'utiliser un handle. Dans le processus principal, cela envoie un message à un processus de travail spécifique. C'est la même chose que ChildProcess.send()
. Dans le processus de travail, cela envoie un message au processus principal. C'est la même chose que process.send()
..destroy()
.kill([signal])
Cette fonction tuera le processus de travail. La fonction kill()
tue le processus de travail sans attendre une déconnexion gracieuse, elle a le même comportement que worker.process.kill()
. Pour des raisons de compatibilité ascendante, cette méthode est alias worker.destroy()
..disconnect([callback])
est envoyé au processus de travail, l'obligeant à appeler son propre .disconnect()
qui arrêtera tous les serveurs, attendra les événements 'close'
sur ces serveurs, puis déconnectera le canal IPC..isConnect()
Cette fonction renvoie true
si le processus de travail est connecté à son processus principal via son canal IPC, false
sinon. Les processus de travail se connectent à leur processus maître après leur création..isDead()
Cette fonction renvoie true
si le processus de travail s'est terminé (en raison de la sortie ou de la réception d'un signal). Sinon, il renvoie false
.Afin de rendre le cluster plus stable et robuste, le module cluster
expose également de nombreux événements :
'message'
, dans le processus de travail.cluster.workers[id].on('message', messageHandler);
'exit'
, lorsqu'un processus de travail meurt,当前worker工作进程
déclenchera l'événement 'exit'
.si (cluster.isPrimary) { const travailleur = cluster.fork(); travailleur.on('sortie', (code, signal) => { si (signal) { console.log(`le travailleur a été tué par le signal : ${signal}`); } sinon si (code !== 0) { console.log(`worker est sorti avec le code d'erreur : ${code}`); } autre { console.log('succès du travailleur !'); } }); }
'listening'
, appelez listen()
depuis le processus de travail pour écouter le processus de travail actuel.cluster.fork().on('écoute', (adresse) => { // Le processus de travail écoute });
disconnect
, qui est déclenché après la déconnexion du canal IPC du processus de travail. Lorsque le processus de travail se termine normalement, est arrêté ou se déconnecte manuellementcluster.fork().on('disconnect', () => { //Limité au déclenchement sur l'objet travailleur actuel});
Dans Node, la communication inter-processus (IPC) est utilisée pour réaliser la communication inter-processus entre le processus principal et les sous-processus. Méthode .send()
(a.send signifie envoyer un message à un Send) pour envoyer des messages et écouter les événements message
pour collecter des informations. Ceci est implémenté par cluster模块
en intégrant EventEmitter
. C'est aussi un simple exemple de communication inter-processus sur le site officiel
process.on('message')
, process.send()
child.on('message')
, child.send()
# cluster.isMaster # cluster.fork() # cluster.workers # cluster.workers[id].on('message', messageHandler); # cluster.workers[id].send(); # process.on('message', messageHandler); # process.send(); const cluster = require('cluster'); const http = exiger('http'); # Processus principal if (cluster.isMaster) { // Gardez une trace des requêtes http console.log(`Primary ${process.pid} est en cours d'exécution`); soit numReqs = 0 ; // Compter les requêtes fonction messageHandler(msg) { if (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1 ; } } // Démarrez les Workers et écoutez les messages contenant notifyRequest // Démarre le multi-processus (nombre de cœurs CPU) // Processus de travail de génération. const numCPUs = require('os').cpus().length; pour (soit i = 0; i < numCPUs; i++) { console.log(i) cluster.fork(); } // Le processus principal du cluster worker communique avec les processus enfants pour (const id dans cluster.workers) { // ***Écoutez les événements des processus enfants cluster.workers[id].on('message', messageHandler); // ***Envoyer cluster.workers[id].send({ au processus enfant tapez : 'maîtreVersTravailleur', de : 'maître', données: { nombre : Math.floor(Math.random() * 50) } }); } cluster.on('exit', (travailleur, code, signal) => { console.log(`worker ${worker.process.pid} est mort`); }); } autre { # Processus enfants // Les processus de travail peuvent partager n'importe quelle connexion TCP // Dans cet exemple, il s'agit d'un serveur HTTP // Les processus de travail ont un serveur http. http.Serveur((req, res) => { res.writeHead(200); res.end('bonjour tout le monden'); //******! ! ! ! Informez le maître de la demande ! ! ! ! ! ! ******* //****** Envoyer process.send({ cmd: 'notifyRequest' }); //****** Écoutez process.on('message', function(message) { // xxxxxxx }) }).écouter(8000); console.log(`Worker ${process.pid} démarré`); }
La communication entre les processus NodeJS implique uniquement la transmission de messages et ne transfère pas réellement d'objets.
Avant d'envoyer le message, la méthode send()
assemblera le message en un handle et un message. Ce message sera sérialisé par JSON.stringify
. C'est-à-dire que lors du passage du handle, l'objet entier ne sera pas transmis. transmis via le canal IPC. Ce sont toutes des chaînes et sont restaurées en objets via JSON.parse
après la transmission.
Il y a app.listen(port)
dans le code lors du forking, pourquoi plusieurs processus peuvent-ils écouter le même port ?
La raison en est que le processus principal envoie le handle d'un objet de service appartenant au processus principal à plusieurs sous-processus via la méthode send(), donc pour chaque sous-processus, après avoir restauré le handle, ils obtiennent le même objet de service. Lorsque le réseau Lorsqu'une requête est adressée au serveur, le service de processus est préemptif, donc aucune exception ne sera provoquée lors de l'écoute sur le même port.
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); pour (soit i=0; i<cpus.length; i++) { const travailleur = fork('worker.js'); console.log('processus de travail créé, pid : %s ppid : %s', travailleur.pid, processus.pid); }
# travailleur.js const http = exiger('http'); http.createServer((req, res) => { res.end('Je suis un travailleur, pid : ' + process.pid + ', ppid : ' + process.ppid); }).listen(3000);
Dans l'exemple de code ci-dessus, lorsque la console exécute
node master.js
un seul travailleur peut écouter le port 3000, et les autres renvoientError: listen EADDRINUSE :::3000
.
发送句柄
entre les processus après la version v0.5.9/**. * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * message * envoyerHandle */ subprocess.send(message, sendHandle)
Une fois le canal IPC établi entre les processus parent et enfant, le message est envoyé via la méthode d'envoi de l'objet sous-processus.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
, afin de résoudre le problème d'occupation de port multi-processus ci-dessus, nous passons le socket du processus principal au processus enfant.
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); const server = require('net').createServer(); serveur.écouter (3000); process.title = 'noeud-maître' pour (soit i=0; i<cpus.length; i++) { const travailleur = fork('worker.js'); # Passez le handle worker.send('server', server); console.log('processus de travail créé, pid : %s ppid : %s', travailleur.pid, processus.pid); }
// travailleur.js laissez le travailleur ; process.title = 'noeud-worker' process.on('message', fonction (message, sendHandle) { if (message === 'serveur') { travailleur = sendHandle ; travailleur.on('connexion', fonction (socket) { console.log('Je suis un travailleur, pid : ' + process.pid + ', ppid : ' + process.ppid) }); } });
Vérifiez que la console exécute node master.js
Si vous comprenez cluster
, vous saurez que les processus enfants sont créés via cluster.fork()
. Sous Linux, le système fournit nativement la méthode fork
, alors pourquoi Node choisit-il d'implémenter cluster模块
par lui-même au lieu d'utiliser directement la méthode native du système ? Les principales raisons sont les deux points suivants :
Le processus fork surveille le même port, ce qui entraînera des erreurs d'occupation du port.
Il n'y a pas d'équilibrage de charge entre les processus fork, ce qui peut facilement conduire à un phénomène de troupeau tumultueux
dans cluster模块
. le premier problème, nous déterminons si le processus actuel est master进程
, si c'est le cas, écoute sur le port. Sinon, il est représenté comme un worker进程
fork et n'écoute pas sur le port.
En réponse à la deuxième question, cluster模块
dispose d'une fonction d'équilibrage de charge intégrée. master进程
est chargé d'écouter le port pour recevoir les demandes, puis de les attribuer aux worker进程
correspondants via l'algorithme de planification (la valeur par défaut est). Round-Robin, l'algorithme de planification peut être modifié via la variable d'environnement NODE_CLUSTER_SCHED_POLICY
).
Lorsque le code lève une exception qui n'est pas interceptée, le processus se termine. À ce moment, Node.js fournit process.on('uncaughtException', handler)
pour l'attraper, mais lorsqu'une exception est détectée. Lorsque le processus Worker rencontre une exception non interceptée, il est déjà dans un état incertain. À ce stade, nous devons laisser le processus se terminer normalement :
+---------+ +---------+ | Travailleur | +---------+ +----+----+ | exception non capturée | +----------------+ | | +---------+ | <----------+ | | +----+----+ | déconnecter | fourche un nouveau travailleur | +-----------------------------> + ----------------------- -> | | attendez... | | sortie | +--------------> | | mourir | | |
Lorsqu'un processus a une exception qui provoque un crash ou un MOO et est tué par le système, contrairement à lorsqu'une exception non interceptée se produit, nous avons toujours la possibilité de laisser le processus continuer à s'exécuter. le processus en cours se termine directement et le maître crée immédiatement un nouveau travailleur.
Le module child_process offre la possibilité de dériver des processus enfants, qui sont simplement执行cmd命令的能力
. Par défaut, stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. Ces pipelines ont une capacité limitée (et spécifique à la plate-forme). Si le processus enfant dépasse cette limite lors de l'écriture sur la sortie standard et qu'aucune sortie n'est capturée, le processus enfant se bloque et attend que le tampon de canal accepte plus de données. C'est le même comportement qu'un tuyau dans le shell. Si la sortie n'est pas consommée, utilisez l'option { stdio: 'ignore' }.
const cp = require('child_process');
Le processus enfant créé via l'API n'a pas de connexion nécessaire avec le processus parent.
4 méthodes asynchrones sont utilisées pour créer des processus enfants : fork, exec, execFile, spawn
Node.
fork(modulePath, args)
: Utilisé lorsque vous souhaitez exécuter un processus Node en tant que processus indépendant, afin que le traitement de calcul et le descripteur de fichier soient séparés du processus principal du Node (copie d'un processus enfant)Non-Node
spawn(command, args)
: Traiter certains problèmes. Utilisez execFile(file, args[, callback]) lorsqu'il y a de nombreuses E/S de sous-processus ou lorsque le processus a une grande quantité de sortieexecFile(file, args[, callback])
Utilisez-le lorsque vous avez uniquement besoin d'exécuter un programme externe. la vitesse d'exécution est rapide et il est relativement sûr de traiter les entrées de l'utilisateur.exec(command, options)
: Utilisé lorsque vous souhaitez accéder directement à la commande shell du thread. Assurez-vous de faire attention auxtrois méthodes de synchronisation saisies par l'utilisateur : execSync
, execFileSync
, spawnSync
Les trois autres méthodes sont des extensions de spawn()
.
. N'oubliez pas que le processus enfant Node.js dérivé est indépendant. Le processus parent, à l'exception du canal de communication IPC établi entre les deux. Chaque processus possède sa propre mémoire et sa propre instance V8
.
Par exemple, créez deux fichiers, worker.js et master.js, dans un répertoire :
# child.js. const t = JSON.parse(process.argv[2]); console.error(`processus enfant t=${JSON.stringify(t)}`); process.send({bonjour:`son pid=${process.pid} s'il te plaît, donne à papa process pid=${process.ppid} bonjour`}); processus.on('message', (msg)=>{ console.error(`processus enfant msg=${JSON.stringify(msg)}`); });
# parent.js const {fork} = require('child_process'); pour(soit i = 0; i < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,name:1})]); p.on('message', (msg) => { console.log(`messsgae from child msg=${JSON.stringify(msg)}`, ); }); p.send({bonjour :`Bonjour de papa ${process.pid} process id=${i}`}); }
Démarrez parent.js via node parent.js
, puis vérifiez le nombre de processus via ps aux | grep worker.js
Nous pouvons constater qu'idéalement, le nombre de processus est égal au nombre de cœurs de processeur et chaque processus en utilise un. Cœur du processeur.
Il s'agit du mode classique Master-Worker (mode maître-esclave)
En fait, créer un processus coûte cher et le but de la copie d'un processus est d'utiliser pleinement les ressources du processeur. NodeJS utilise donc une approche basée sur les événements sur un seul thread pour résoudre le problème de la concurrence élevée.
Scénarios applicables <br/>Généralement utilisés pour les scénarios qui prennent du temps et sont implémentés à l'aide d'un nœud, comme le téléchargement de fichiers ;
Fork peut implémenter le téléchargement multithread : divisez le fichier en plusieurs blocs, puis chaque processus télécharge une partie et les rassemble enfin ;
const cp = require('child_process'); // Le premier paramètre est le nom ou le chemin du fichier exécutable à exécuter. voici l'écho cp.execFile('echo', ['hello', 'world'], (err, stdout, stderr) => { si (erreur) { console.erreur (erreur) ; console.log('stdout: ', stdout); console.log('stderr: ', stderr); });
Scénarios applicables <br/> Plus adapté aux tâches avec une faible surcharge et plus d'attention aux résultats, telles que ls, etc.
est principalement utilisé pour exécuter une méthode shell, et spawn est toujours appelé en interne, mais il existe une limite maximale de cache.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | sort | uniq`, (err, stdout, stderr) => { console.log(stdout); });
Scénarios applicables <br/>Plus adapté aux tâches avec une faible surcharge et accordant plus d'attention aux résultats, telles que ls, etc. ;
tâche unique
d'interface de flux d'E/Sconst cp = require('child_process'); const child = cp.spawn('echo', ['hello', 'world']); enfant.on('erreur', console.erreur); # La sortie est un flux, sortie vers le processus principal stdout, la console child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
Concaténation multitâche
const cp = require('child_process'); const chemin = require('chemin'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); const sort = cp.spawn('tri'); const uniq = cp.spawn('uniq'); #La sortie est un flux cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout);
Scénarios applicables
spawn est en streaming, il convient donc aux tâches fastidieuses, telles que l'exécution de npm install et l'impression du processus d'installation
est déclenché une fois le processus terminé et le flux d'entrée et de sortie standard (sdtio) du le processus enfant a été 'close'
. Cet événement est différent de exit
car plusieurs processus peuvent partager le même flux stdio.
Paramètres :
Question : Le code doit-il exister ?
(Cela ne semble pas être le cas d'après les commentaires sur le code) Par exemple, si vous utilisez kill
pour tuer le processus enfant, quel est le code ?
paramètres de sortie :
code, signal, si le processus enfant se termine tout seul, alors code
est le code de sortie, sinon il est nul ;
Si le processus enfant se termine via un signal, alors signal
est le signal pour terminer le processus, sinon il est nul.
Des deux, l’un ne doit pas être nul.
Choses à noter :
Lorsque l'événement exit
est déclenché, le flux stdio du processus enfant peut toujours être ouvert. (Scénario ?) De plus, nodejs écoute les signaux SIGINT et SIGTERM, c'est-à-dire que lorsque nodejs reçoit ces deux signaux, il ne se fermera pas immédiatement, il effectuera d'abord un travail de nettoyage, puis renverra ces deux signaux. (Visuellement, js peut effectuer un travail de nettoyage à ce moment-là, comme fermer la base de données, etc.)
SIGINT
: interruption, signal de fin de programme, généralement émis lorsque l'utilisateur appuie sur CTRL+C, utilisé pour notifier au processus de premier plan de terminer le processus.
SIGTERM
: terminer, signal de fin de programme, ce signal peut être bloqué et traité, et est généralement utilisé pour exiger que le programme se termine normalement. La commande shell kill génère ce signal par défaut. Si le signal ne peut pas être terminé, nous essaierons SIGKILL (terminaison forcée).
Lorsque les choses suivantes se produisent, une erreur sera déclenchée. Lorsqu'une erreur se déclenche, la sortie peut se déclencher ou non. (Le cœur est brisé)
est déclenché lorsque process.send()
est utilisé pour envoyer un message.
paramètre :
message
, est un objet json ou une valeur primitive ; sendHandle
, un objet net.Socket ou un objet net.Server (les étudiants familiers avec le cluster devraient le connaître)
: lors de l'appel de .disconnected()
, définissez-le à faux. Représente s'il peut recevoir des messages du processus enfant ou envoyer des messages au processus enfant.
.disconnect() : ferme le canal IPC entre le processus parent et le processus enfant. Lorsque cette méthode est appelée, l'événement disconnect
se déclenche. Si le processus enfant est une instance de nœud (créée via child_process.fork()), alors process.disconnect()
peut également être activement appelé à l'intérieur du processus enfant pour mettre fin au canal IPC.
répond aux problèmes monothread. Les méthodes multi-processus sont généralement utilisées pour simuler le multithread.
sont occupés par le processus Node. Le cœur du
nœud à 7 threads est le moteur v8. Une fois le nœud démarré, cette instance est le
.单线程
, mais l'environnement hôte de Javascript, qu'il s'agisse de Node ou du navigateur, est multithread.
Pourquoi Javascript est-il monothread ?
Ce problème doit commencer avec le navigateur. Pour les opérations DOM dans l'environnement du navigateur, imaginez simplement que si plusieurs threads fonctionnent sur le même DOM, cela signifie que l'opération DOM ne peut être effectuée que d'une seule manière. évitez les conflits de rendu DOM. Dans l'environnement du navigateur, le thread de rendu de l'interface utilisateur et le moteur d'exécution JS s'excluent mutuellement. Lorsque l'un est exécuté, l'autre sera suspendu. Ceci est déterminé par le moteur JS.
process.env.UV_THREADPOOL_SIZE. = 64
worker_threads
pour fournir de véritables capacités multi-threading à Node.const {. estMainThread, port parent, données de travailleur, ID de fil, Canal de messages, Port de messages, Travailleur } = require('worker_threads'); fonction filmain() { pour (soit i = 0; i < 5; i++) { const travailleur = nouveau travailleur (__filename, { travailleurData : i }); travailleur.on('exit', code => { console.log(`main : travailleur arrêté avec le code de sortie ${code}`); }); travailleur.on('message', msg => { console.log(`main : recevoir ${msg}`); travailleur.postMessage(msg + 1); }); } } fonction workThread() { console.log(`worker: workerDate ${workerData}`); parentPort.on('message', msg => { console.log(`worker : recevoir ${msg}`); }), parentPort.postMessage(workerData); } si (isMainThread) { fil principal(); } autre { travailleurThread(); }
const assert = require('assert'); const { Travailleur, Canal de messages, Port de messages, estMainThread, port parent } = require('worker_threads'); si (isMainThread) { const travailleur = nouveau travailleur (__filename); const subChannel = new MessageChannel(); travailleur.postMessage({ hereIsYourPort : subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (valeur) => { console.log('reçu :', valeur); }); } autre { parentPort.once('message', (valeur) => { assert (value.hereIsYourPort instance de MessagePort); value.hereIsYourPort.postMessage('le travailleur envoie ceci'); valeur.hereIsYourPort.close(); }); }
Le processus est la plus petite unité d'allocation de ressources et le thread est la plus petite unité de planification du processeur.
IPC (Inter-process communication) est进程间通信
. Étant donné que chaque processus a son propre espace d'adressage indépendant après sa création, le but de la mise en œuvre d'IPC est de partager l'accès aux ressources entre les processus.
Il existe de nombreuses façons d'implémenter IPC : les canaux, les files d'attente de messages, les sémaphores, les Domain Sockets et Node.js est implémenté via les canaux.
En fait, le processus parent créera d'abord un canal IPC et écoutera cet IPC avant de créer le processus enfant, puis créera le processus enfant. Il indiquera au processus enfant et au descripteur de fichier lié au canal IPC via la variable d'environnement (. NODE_CHANNEL_FD). Le processus enfant démarre. À ce moment, le canal IPC est connecté selon le descripteur de fichier pour établir une connexion avec le processus parent.
Un handle est une référence qui peut être utilisée pour identifier une ressource. Il contient le descripteur de ressource de fichier pointant vers l'objet.
Généralement, lorsque nous souhaitons surveiller plusieurs processus sur un seul port, nous pouvons envisager d'utiliser l'agent de processus principal :
Cependant, cette solution proxy fera en sorte que chaque réception de requête et transfert proxy utilise deux descripteurs de fichiers, et les descripteurs de fichiers du système sont limités. Cette approche affectera l'évolutivité du système.
Alors pourquoi utiliser des poignées ? La raison en est que dans les scénarios d'application réels, l'établissement d'une communication IPC peut impliquer des scénarios de traitement de données plus complexes. Le handle peut être transmis comme deuxième paramètre facultatif de la méthode send()
, ce qui signifie que l'identifiant de ressource peut être transmis directement. la transmission évite l’utilisation de descripteurs de fichiers causée par le transfert proxy mentionné ci-dessus.
Voici les types de handles qui prennent en charge l'envoi :
Une fois que le processus parent du processus orphelin a créé un processus enfant, le processus parent se termine, mais un ou plusieurs processus enfants Les processus correspondant au processus parent sont toujours en vie. Ceci est illustré par l'exemple de code suivant.
# worker.js const http = requis ('http'); const Server = http.createServer ((req, res) => { res.end ('je suis un travailleur, pid:' + process.pid + ', ppid:' + process.ppid); // Enregistrez le processus de travail actuel PID et le processus parent PPID }); Laisser le travailleur; process.on ('message', fonction (message, sendhandle) { if (message === 'server') { Worker = SendHandle; wearch.on ('connexion', fonction (socket) { server.emit («connexion», socket); }); } });
# Master.js const fork = require ('child_process'). Fork; const Server = require ('net'). CreateServer (); server.Listen (3000); const Worker = Fork ('worker.js'); Worker.Send ('Server', Server); Console.log («Processus de travail créé, PID:% s ppid:% s», worker.pid, process.pid); process.exit (0); // Après avoir créé le processus enfant, le processus principal sort
.
Étant donné que le processus parent est sorti dans Master.js, le moniteur d'activité ne montre que le processus des travailleurs.
Vérifiez à nouveau, ouvrez l'interface d'appel de la console, vous pouvez voir que le PPID correspondant au processus du travailleur 5611 est 1 (pour le processus d'initial), et il est devenu un processus orphelin pour le moment.
Le processus de démon fonctionne en arrière-plan et n'est pas affecté par le terminal.
Les étudiants qui développent Node.js node app.js
le familiariser.前台运行模式
.
Si la méthode du processus de démon est utilisée, après avoir exécuté node app.js
pour démarrer un processus de service dans ce terminal, je peux également faire d'autres choses sur ce terminal sans s'affaire.
Créer un processus d'enfant
Créer une nouvelle session dans le processus de l'enfant (appeler le System Fonction SetSid)
Modifier le répertoire de travail du processus de l'enfant (tel que: "/" ou "/ usr /, etc.)
terminer le processus parent
options.detached
. const Spawn = require ('child_process'). Spawn; fonction startDaemon () { const daemon = spawn ('nœud', ['daemon.js'], { cwd: '/ usr', détaché: vrai, stdio: «ignorer», }); Console.log ('Daemon Process démarre le processus parent PID:% S, Daemon Process Pid:% S', process.pid, daemon.pid); daemon.UnRef (); } startDaemon ()La logique de traitement dans le fichier daemon.js démarre un temporisateur
et
l'exécute toutes les 10 secondes afin que cette ressource ne sorte pas.
du processus enfant.const fs = require('fs'); const {console} = require ('console'); // Enregistreur simple personnalisé const logger = nouvelle console (fs.createwRitestream ('./ stdout.log'), fs.createwRitestream ('./ stderr.log')); setInterval (function () { Logger.log ('Daemon Pid:', process.pid, ', ppid:', process.ppid);},
1000
*
10);
dans le travail réel, nous ne sommes pas étrangers aux processus de démon, tels que PM2, cluster d'oeufs, etc. Ce qui précède est juste une simple démo pour expliquer le processus de démon. Le processus de démon est encore très élevé.
5.Quel est le
répertoirede process.cwd()
À partir du répertoire du processus parent, qui peut être obtenu par process.chdir()
Que
fait-il?
Le résultat correct ne sera pas obtenu. Dans un autre cas, le module tiers référencé dans le programme est également recherché en fonction du répertoire où le processus actuel est démarré.
// Exemple process.chdir ('/ utilisateurs / peut / documents / test /') // définir le répertoire de processus actuel console.log (process.cwd ());