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 :
exec(commande,rappel);
// 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)
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 :
Délai d'expiration : 1000,
killSignal : 'SIGKILL'
};
//…
});
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 :
.
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.
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)
envCopy['CUSTOM ENV VAR1'] = 'une valeur';
envCopy['CUSTOM ENV VAR2'] = 'une autre valeur';
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)
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);
});
Exemple 8-4 : Processus enfant analysant les variables d'environnement (chapter8/04_environment_number_child.js)
numéro = parseInt(numéro, 10);
console.log(typeof(number)); // → "numéro"
numéro
stderr :
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
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)
// 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 :
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 :
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)
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 :
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)
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)
// 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)
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)
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 :
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 :
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 :
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 :
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.