Maison  >  Article  >  interface Web  >  Explication détaillée de la création et de la gestion de processus externes dans Node.js_node.js

Explication détaillée de la création et de la gestion de processus externes dans Node.js_node.js

WBOY
WBOYoriginal
2016-05-16 16:39:421180parcourir

Node est conçu pour gérer efficacement les opérations d'E/S, mais vous devez savoir que certains types de programmes ne sont pas adaptés à ce modèle. Par exemple, si vous envisagez d'utiliser Node pour gérer une tâche gourmande en CPU, vous risquez d'obstruer la boucle d'événements et donc de réduire la réactivité du programme. L'alternative consiste à confier les tâches gourmandes en CPU à un processus distinct, libérant ainsi la boucle d'événements. Node vous permet de générer un processus et de faire du nouveau processus un enfant de son parent. Dans Node, le processus enfant peut communiquer avec le processus parent dans les deux sens et, dans une certaine mesure, le processus parent peut également surveiller et gérer le processus enfant.

Une autre situation dans laquelle vous devez utiliser un sous-processus est lorsque vous souhaitez simplement exécuter une commande externe et laisser Node obtenir la valeur de retour de la commande. Par exemple, vous pouvez exécuter une commande, un script ou d'autres commandes UNIX qui ne peuvent pas être exécutées directement dans Node.

Ce chapitre vous montrera comment exécuter des commandes externes, créer et communiquer avec des processus enfants et mettre fin aux processus enfants. Le but est de vous permettre de comprendre comment effectuer une série de tâches en dehors du processus Node.

Exécuter la commande externe

Lorsque vous devez exécuter une commande shell externe ou un fichier exécutable, vous pouvez utiliser le module child_process et l'importer comme ceci :

Copier le code Le code est le suivant :

var child_process = require('child_process')

Ensuite, vous pouvez utiliser la fonction exec au sein du module pour exécuter des commandes externes :
Copier le code Le code est le suivant :

var exec = enfant_processus.exec;

exec(commande,rappel);


Le premier paramètre de exec est la chaîne de commande shell que vous souhaitez exécuter et le deuxième paramètre est une fonction de rappel. Cette fonction de rappel sera appelée lorsque exec aura fini d'exécuter la commande externe ou qu'une erreur se produira. La fonction de rappel a trois paramètres : error, stdout, stderr, voir l'exemple suivant :
Copier le code Le code est le suivant :

exec('ls',function(err,stdout,stderr){

                               // Note du traducteur : si vous utilisez Windows, vous pouvez passer aux commandes Windows, telles que dir, qui ne seront pas décrites à nouveau

});

Si une erreur se produit, le premier paramètre sera une instance de la classe Error. Si le premier paramètre ne contient pas d'erreur, alors le deuxième paramètre stdout contiendra la sortie standard de la commande. Le dernier paramètre contient une sortie d'erreur liée à la commande.

Le Listing 8-1 montre un exemple plus complexe d'exécution de commandes externes

LISTING 8-1 : Exécuter des commandes externes (code source : chapitre8/01_external_command.js)

Copier le code Le code est le suivant :

//Importer la fonction exec du module child_process
var exec = require('child_process').exec;
//Appelle la commande "cat *.js | wc -l"
exec('cat *.js | wc –l ', function(err, stdout, stderr ){ //La quatrième ligne
//La commande se termine ou l'appel échoue
Si(erreur){
//Échec du démarrage du processus externe
                  console.log(‘child_process s'est arrêté, le code d'erreur est : ',err.code);
               revenir ;
         }
>

Sur la quatrième ligne, nous passons "cat *.js | wc -l" comme premier paramètre à exécuter. Vous pouvez également essayer n'importe quelle autre commande, à condition d'avoir utilisé la commande dans le shell.

Transmettez ensuite une fonction de rappel comme deuxième paramètre, qui sera appelée lorsqu'une erreur se produit ou que le processus enfant se termine.

Vous pouvez également passer un troisième paramètre facultatif avant la fonction de rappel, qui contient certaines options de configuration, telles que :

Copier le code Le code est le suivant :

var exec = require('child_process').exec; options var ={

Délai d'expiration : 1000,
         killSignal : 'SIGKILL'
};

exec('cat *.js | wc –l ', options, fonction(err,stdout,stderr){

              //…
});

Les paramètres pouvant être utilisés sont :

1.cwd - Répertoire actuel, vous pouvez spécifier le répertoire de travail actuel.

2.encoding - le format de codage du contenu de sortie du processus enfant. La valeur par défaut est "utf8", qui est le codage UTF-8. Si la sortie du processus enfant n'est pas utf8, vous pouvez utiliser ce paramètre pour le définir. Les formats d'encodage pris en charge sont :
.

Copier le code Le code est le suivant :
ascii
utf8
ucs2
base64

Si vous souhaitez en savoir plus sur ces formats d'encodage pris en charge par Node, veuillez vous référer au chapitre 4 «

Utiliser Buffer pour traiter, encoder et décoder des données binaires ».

1.timeout - délai d'expiration de l'exécution de la commande en millisecondes, la valeur par défaut est 0, ce qui signifie aucune limite, jusqu'à la fin du processus enfant.

2.maxBuffer - Spécifiez le nombre maximum d'octets autorisés à être générés par le flux stdout et le flux stderr. Si la valeur maximale est atteinte, le processus enfant sera tué. La valeur par défaut est 200*1024.
3.killSignal - Un signal de fin envoyé au processus enfant lorsqu'il expire ou que le tampon de sortie atteint sa taille maximale. La valeur par défaut est "SIGTERM", qui enverra un signal de fin au processus enfant. Cette approche ordonnée est généralement utilisée pour mettre fin aux processus. Lors de l'utilisation du signal SIGTERM, le processus peut le traiter ou remplacer le comportement par défaut du processeur de signal après l'avoir reçu. Si le processus cible en a besoin, vous pouvez lui transmettre d'autres signaux (tels que SIGUSR1) en même temps. Vous pouvez également choisir d'envoyer un signal SIGKILL, qui sera traité par le système d'exploitation et forcera la fin immédiate du processus enfant. Dans ce cas, aucune opération de nettoyage du processus enfant ne sera effectuée.

Si vous souhaitez contrôler davantage la fin du processus, vous pouvez utiliser la commande child_process.spawn, qui sera présentée plus tard.

1.evn - Spécifie les variables d'environnement transmises au processus enfant. La valeur par défaut est null, ce qui signifie que le processus enfant héritera des variables d'environnement de tous les processus parents avant sa création.

Remarque : en utilisant l'option killSignal, vous pouvez envoyer un signal au processus cible sous la forme d'une chaîne. Les signaux existent sous forme de chaînes dans Node. Voici une liste des signaux UNIX et des opérations par défaut correspondantes :

Vous souhaiterez peut-être fournir au processus enfant un ensemble extensible de variables d'environnement parent. Si vous modifiez directement l'objet process.env, vous modifierez les variables d'environnement de tous les modules du processus Node, ce qui causera beaucoup de problèmes. L'alternative consiste à créer un nouvel objet et à copier tous les paramètres dans process.env, voir Exemple 8-2 :

LISTING 8-2 : Utiliser des variables d'environnement paramétrées pour exécuter des commandes (code source : chapitre8/02_env_vars_augment.js)

Copier le code Le code est le suivant :
var env = processus.env,
varName,
envCopie = {},
exec = require('child_prcess').exec;
// Copier process.env dans envCopy
pour(vaName dans ev){
envCopy[varName] = env[varName];
>
//Définissez des variables personnalisées

envCopy['CUSTOM ENV VAR1'] = 'une valeur';
envCopy['CUSTOM ENV VAR2'] = 'une autre valeur';

//Utilisez process.env et des variables personnalisées pour exécuter des commandes

exec('ls –la',{env: envCopy}, function(err,stdout,stderr){
Si(err){ throw err; }
console.log('stdout:', stdout);
console.log('stderr:',stderr);
>

L'exemple ci-dessus crée une variable envCopy pour enregistrer les variables d'environnement. Il copie d'abord les variables d'environnement du processus Node à partir de process.env, puis ajoute ou remplace certaines variables d'environnement qui doivent être modifiées, et enfin utilise envCopy comme environnement. . Les arguments variables sont transmis à la fonction exec et la commande externe est exécutée.

N'oubliez pas que les variables d'environnement sont transmises entre les processus via le système d'exploitation et que tous les types de valeurs de variables d'environnement parviennent au processus enfant sous forme de chaînes. Par exemple, si le processus parent contient le nombre 123 comme variable d'environnement, le processus enfant recevra "123" sous forme de chaîne.

L'exemple suivant créera deux scripts Node dans le même répertoire : parent.js et child.js. Le premier script appellera le second. Créons ces deux fichiers :

.

LISTING 8-3 : Le processus parent définit les variables d'environnement (chapter8/03_environment_number_parent.js)

Copier le code Le code est le suivant :

var exec = require('child_process').exec;

exec('node child.js', {env : {number : 123}}, function(err, stdout, stderr) {

if (err) { throw err;🎜>

console.log('stdout:n', stdout);

console.log('stderr:n', stderr);

});


Enregistrez ce code dans parent.js. Voici le code source du processus enfant et enregistrez-le dans child.js (voir exemple 8-4)

Exemple 8-4 : Processus enfant analysant les variables d'environnement (chapter8/04_environment_number_child.js)

Copier le code Le code est le suivant :
var numéro = process.env.numéro;
console.log(typeof(number)); // → "string"

numéro = parseInt(numéro, 10);

console.log(typeof(number)); // → "numéro"


Après avoir enregistré ce fichier sous child.js, vous pouvez exécuter la commande suivante dans ce répertoire :

Copier le code Le code est le suivant :
$ noeud parent.js

Vous verrez le résultat suivant :

Copier le code Le code est le suivant :
sdtou :
chaîne

numéro

stderr :


Comme vous pouvez le voir, bien que le processus parent transmette une variable d'environnement numérique, le processus enfant la reçoit sous forme de chaîne (voir la deuxième ligne de sortie). Sur la troisième ligne, vous analysez la chaîne en un nombre.

Générer un processus enfant

Comme vous pouvez le voir, vous pouvez utiliser la fonction child_process.exec() pour démarrer un processus externe et appeler votre fonction de rappel lorsque le processus se termine. C'est très simple à utiliser, mais cela présente également certains inconvénients :

1. En plus d'utiliser les paramètres de ligne de commande et les variables d'environnement, exec() ne peut pas communiquer avec les processus enfants

2. La sortie du processus enfant est mise en cache, vous ne pouvez donc pas la diffuser, elle risque de manquer de mémoire

Heureusement, le module child_process de Node permet un contrôle plus précis du démarrage, de l'arrêt et d'autres opérations générales des processus enfants. Vous pouvez démarrer un nouveau processus enfant dans l'application et Node fournit un canal de communication bidirectionnel qui permet au processus parent et au processus enfant de s'envoyer et de recevoir des données de chaîne. Le processus parent peut également effectuer certaines opérations de gestion sur le processus enfant, envoyer des signaux au processus enfant et forcer la fermeture du processus enfant.

Créer un processus enfant

Vous pouvez utiliser la fonction child_process.spawn pour créer un nouveau processus enfant, voir Exemple 8-5 :

Exemple 8-5 : générer un processus enfant. (chapitre8/05_spawning_child.js)


Copier le code Le code est le suivant :
// Importer la fonction spawn du module child_process
var spawn = require('child_process').spawn;

// Générer un processus enfant pour exécuter la commande "tail -f /var/log/system.log"

var child = spawn('tail', ['-f', '/var/log/system.log']);


Le code ci-dessus génère un sous-processus pour exécuter la commande tail et prend "-f" et "/bar/log/system.log" comme paramètres. La commande tail surveillera le fichier /var/log/system.og (s'il existe) et affichera toutes les nouvelles données ajoutées dans le flux de sortie standard stdout. La fonction spawn renvoie un objet ChildProcess, qui est un objet pointeur qui encapsule l'interface d'accès du processus réel. Dans cet exemple, nous attribuons ce nouveau descripteur à une variable appelée enfant.

Écouter les données des processus enfants

Tout handle de processus enfant contenant l'attribut stdout utilisera la sortie standard du processus enfant comme objet de flux. Vous pouvez lier l'événement de données à cet objet de flux, de sorte que chaque fois qu'un bloc de données est disponible, le rappel correspondant. fonction, voir l'exemple ci-dessous :

Copier le code Le code est le suivant :

//Imprime la sortie du processus enfant sur la console

child.stdout.on('data',function(data){

console.log('tail output:' data);

});

Chaque fois que le processus enfant envoie des données sur la sortie standard, le processus parent sera averti et imprimera les données sur la console.

En plus de la sortie standard, le processus a un autre flux de sortie par défaut : le flux d'erreur standard. Ce flux est généralement utilisé pour générer des informations sur les erreurs.

Dans cet exemple, si le fichier /var/log/system.log n'existe pas, le processus tail affichera un message similaire au suivant : "/var/log/system.log : Aucun fichier ou répertoire de ce type" , en surveillant le flux stderr, le processus parent sera averti lorsqu'une telle erreur se produit.

Le processus parent peut écouter le flux d'erreur standard comme ceci :

Copier le code Le code est le suivant :

enfant.stderr.on('données', fonction(données) {

console.log('tail error output:', data);

});

L'attribut stderr, comme stdout, est également un flux en lecture seule. Chaque fois que le processus enfant génère des données dans le flux d'erreur standard, le processus parent est averti et génère les données.

Envoyer les données au processus enfant

En plus de recevoir des données du flux de sortie du processus enfant, le processus parent peut également écrire des données sur l'entrée standard du processus enfant via la propriété childPoces.stdin, envoyant ainsi des données vers et depuis le processus enfant.

Le processus enfant peut surveiller les données d'entrée standard via le flux en lecture seule process.stdin, mais notez que vous devez d'abord reprendre le flux d'entrée standard car il est dans un état de pause par défaut.

L'exemple 8 à 6 créera un programme qui contient les fonctions suivantes :

Application 1.1 : une application simple qui peut recevoir des entiers à partir d'une entrée standard, les ajouter, puis afficher le résultat ajouté dans le flux de sortie standard. En tant que simple service informatique, cette application simule le processus Node en tant que service externe pouvant effectuer un travail spécifique.

2. Testez le client d'une application, envoyez des entiers aléatoires, puis affichez les résultats. Utilisé pour démontrer comment le processus Node génère un processus enfant et lui permet ensuite d'effectuer des tâches spécifiques.

Créez un fichier nommé plus_one.js en utilisant le code de l'exemple 8-6 ci-dessous :

Exemple 8-6 : 1 application (chapter8/06_plus_one.js)

Copier le code Le code est le suivant :

//Restaurer le flux d'entrée standard qui est mis en pause par défaut
process.stdin.resume();
process.stdin.on('data', function(data) {
numéro de variable ;
Essayez {
                 // Analyser les données d'entrée en type entier
Nombre = parseInt(data.toString(), 10);
               // 1
nombre = 1;
//Résultat de sortie
Process.stdout.write(numéro "n");
} attraper (erreur) {
Process.stderr.write(err.message "n");
>
});

Dans le code ci-dessus, nous attendons les données du flux d'entrée standard stdin. Chaque fois que des données sont disponibles, nous supposons qu'il s'agit d'un entier et les analysons en une variable entière, puis ajoutons 1 et envoyons le résultat dans le flux de sortie standard. .

Vous pouvez exécuter ce programme avec la commande suivante :

Copier le code Le code est le suivant :

$noeud plus_one.js

Après l'exécution, le programme commence à attendre la saisie. Si vous entrez un nombre entier et appuyez sur Entrée, vous verrez un nombre ajouté par 1 affiché à l'écran.

Vous pouvez quitter le programme en appuyant sur Ctrl-C.

Un client test

Vous devez maintenant créer un processus Node pour utiliser les services informatiques fournis par la précédente "1 application".

Créez d'abord un fichier nommé plus_one_test.js. Le contenu est présenté dans l'exemple 8-7 :

Exemple 8-7 : Application de test 1 (chapter8/07_plus_one_test.js)

Copier le code Le code est le suivant :

var spawn = require('child_process').spawn;
// Générer un processus enfant pour exécuter 1 application
var enfant = spawn('node', ['plus_one.js']);
// Appelle la fonction toutes les secondes
setInterval(fonction() {
// Crée un nombre aléatoire inférieur à 10.000
var number = Math.floor(Math.random() * 10000);
// Envoie ce numéro au processus enfant :
child.stdin.write(numéro "n");
// Récupère la réponse du processus enfant et imprime-la :
child.stdout.once('data', function(data) {
console.log('l'enfant a répondu au 'numéro' avec : 'data);
});
}, 1000);
enfant.stderr.on('données', fonction(données) {
Process.stdout.write(data);
});

Un sous-processus utilisé pour exécuter "1 application" est démarré de la première à la quatrième ligne, puis la fonction setInterval est utilisée pour effectuer les opérations suivantes une fois par seconde :

1.. Créer un nouveau nombre aléatoire inférieur à 10000
2. Transmettez ce numéro sous forme de chaîne au processus enfant
3. Attendez que le processus enfant réponde avec une chaîne
4. Parce que vous souhaitez recevoir uniquement le résultat du calcul d'un seul nombre à la fois, vous devez utiliser child.stdout.once au lieu de child.stdout.on. Si ce dernier est utilisé, une fonction de rappel pour l'événement de données sera enregistrée toutes les 1 secondes. Chaque fonction de rappel enregistrée sera exécutée lorsque la sortie standard du processus enfant recevra des données, vous constaterez donc que le même résultat de calcul sera généré. Souvent, ce comportement est clairement erroné.

Recevoir une notification lorsque le processus enfant se termine

Lorsque le processus enfant se termine, l'événement de sortie sera déclenché. L'exemple 8-8 montre comment l'écouter :

Exemple 8-8 : Écoute de l'événement de sortie du processus enfant (chapter8/09_listen_child_exit.js)

Copier le code Le code est le suivant :

var spawn = require('child_process').spawn;
// Génère un processus enfant pour exécuter la commande "ls -la"
var enfant = spawn('ls', ['-la']);
child.stdout.on('data', function(data) {
console.log('données de l'enfant : 'données);
});

// Lorsque le processus enfant se termine :
child.on('exit', function(code) {
console.log('processus enfant terminé avec le code ' code);
});

Dans les dernières lignes de code en gras, le processus parent utilise l'événement de sortie du processus enfant pour écouter son événement de sortie. Lorsque l'événement se produit, la console affiche la sortie correspondante. Le code de sortie du processus enfant sera transmis à la fonction de rappel comme premier paramètre. Certains programmes utilisent un code de sortie différent de zéro pour représenter certaines conditions d'échec. Par exemple, si vous essayez d'exécuter la commande "ls –al click filename.txt" mais que le fichier n'existe pas dans le répertoire courant, vous obtiendrez un code de sortie de 1, voir Exemple 8-9 :

Exemple 8-9 : Obtenir le code de sortie du processus enfant (chapter8/10_child_exit_code.js)

Copier le code Le code est le suivant :

var spawn = require('child_process').spawn;
// Générer un processus enfant et exécuter la commande "ls makes_not_exist.txt"
var enfant = spawn('ls', ['does_not_exist.txt']);
// Lorsque le processus enfant se termine
enfant.on('exit', function(code) {
console.log('processus enfant terminé avec le code ' code);
});

Dans cet exemple, l'événement exit déclenche la fonction de rappel et lui transmet le code de sortie du processus enfant comme premier paramètre. Si le processus enfant se termine anormalement parce qu'il a été tué par un signal, le code de signal correspondant sera transmis à la fonction de rappel comme deuxième paramètre, comme dans l'exemple 8-10 :

LISTING 8-10 : Obtenir le signal de sortie du processus enfant (chapter8/11_child_exit_signal.js)

Copier le code Le code est le suivant :

var spawn = require('child_process').spawn;
// Générer un processus enfant et exécuter la commande "sleep 10"
var enfant = spawn('sleep', ['10']);
setTimeout(fonction() {
enfant.kill();
}, 1000);
child.on('exit', function(code, signal) {
Si (code) {
console.log('processus enfant terminé avec le code ' code);
} sinon si (signal) {
console.log('processus enfant terminé à cause du signal ' signal);
>
});

Dans cet exemple, un processus enfant est démarré pour effectuer l'opération de mise en veille pendant 10 secondes, mais un signal SIGKILL est envoyé au processus enfant avant 10 secondes, ce qui entraînera le résultat suivant :

Copier le code Le code est le suivant :

processus enfant terminé en raison du signal SIGTERM

Envoyer un signal et tuer le processus

Dans cette partie, vous apprendrez à utiliser des signaux pour gérer les processus enfants. Les signaux sont un moyen simple pour un processus parent de communiquer avec un processus enfant, voire de le tuer.

Différents codes de signal représentent différentes significations. Il existe de nombreux signaux, certains des plus courants sont utilisés pour tuer des processus. Si un processus reçoit un signal qu’il ne sait pas gérer, le programme sera interrompu anormalement. Certains signaux seront gérés par des processus enfants, tandis que d'autres ne pourront être gérés que par le système d'exploitation.

Généralement, vous pouvez utiliser la méthode child.kill pour envoyer un signal au processus enfant. Le signal SIGTERM est envoyé par défaut :

Copier le code Le code est le suivant :

var spawn = require('child_process').spawn;
var enfant = spawn('sleep', ['10']);
setTimeout(fonction() {
enfant.kill();
}, 1000);

Vous pouvez également envoyer un signal spécifique en passant une chaîne identifiant le signal comme seul paramètre de la méthode kill :

Copier le code Le code est le suivant :

enfant.kill('SIGUSR2');

Il est à noter que bien que le nom de cette méthode soit kill, le signal envoyé ne tue pas nécessairement le processus enfant. Si le processus enfant gère le signal, le comportement du signal par défaut est remplacé. Les sous-processus écrits dans Node peuvent réécrire la définition des gestionnaires de signaux comme suit :

Copier le code Le code est le suivant :

processus.on('SIGUSR2', function() {
console.log('J'ai reçu un signal SIGUSR2');
});

Maintenant que vous avez défini le gestionnaire de signal SIGUSR2, lorsque votre processus recevra à nouveau le signal SIGUSR2, il ne sera pas tué, mais affichera la phrase « Vous avez un signal SIGUSR2 ». En utilisant ce mécanisme, vous pouvez concevoir un moyen simple de communiquer avec le processus enfant et même de le commander. Bien qu’elle ne soit pas aussi riche que l’utilisation d’une entrée standard, cette méthode est beaucoup plus simple.

Résumé

Dans ce chapitre, nous avons appris à utiliser la méthode child_process.exec pour exécuter des commandes externes. Cette méthode n'utilise pas de paramètres de ligne de commande, mais transmet les paramètres au processus enfant en définissant des variables d'environnement.

Vous avez également appris à appeler des commandes externes en appelant la méthode child_process.spawn pour générer un processus enfant. De cette façon, vous pouvez utiliser des flux d'entrée et des flux de sortie pour communiquer avec le processus enfant, ou utiliser des signaux pour communiquer avec et. tuer le processus enfant.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn