El nodo está diseñado para manejar las operaciones de E/S de manera eficiente, pero debe saber que algunos tipos de programas no son adecuados para este modo. Por ejemplo, si planea usar el nodo para manejar una tarea intensiva en CPU, puede bloquear el bucle de eventos y así reducir la respuesta del programa. Una alternativa es asignar tareas intensivas en CPU a un proceso separado para manejar, liberando así el bucle de eventos. Node le permite generar un proceso y usar este nuevo proceso como hijo de su proceso principal. En el nodo, el proceso infantil puede comunicarse en dos vías con el proceso principal y, en cierta medida, el proceso principal también puede monitorear y administrar el proceso del niño.
Otro caso en el que necesita usar un proceso infantil es cuando simplemente desea ejecutar un comando externo y dejar que el nodo obtenga el valor de retorno del comando. Por ejemplo, puede ejecutar un comando UNIX, script u otros comandos que no se pueden ejecutar directamente en el nodo.
Este capítulo le mostrará cómo ejecutar comandos externos, crear y comunicarse con los niños y terminar con los niños. El punto es darle una idea de cómo completar una serie de tareas fuera del proceso de nodo. Ejecutar comandos externos Cuando necesita ejecutar un comando o ejecutable de shell externo, puede usar el módulo child_process para importarlo así: La copia del código es la siguiente: var child_process = require ('child_process') Luego puede usar la función EXEC en el módulo para ejecutar comandos externos: La copia del código es la siguiente: var exec = child_process.exec; exec (comando, devolución de llamada); El primer parámetro de EXEC es la cadena de comando shell que está preparando para ejecutar, y el segundo parámetro es una función de devolución de llamada. Esta función de devolución de llamada se llamará cuando Exec haya terminado de ejecutar comandos externos o se produce un error. La función de devolución de llamada tiene tres parámetros: error, stdout, stderr, consulte el siguiente ejemplo: La copia del código es la siguiente: exec ('ls', function (err, stdout, stderr) { // Nota del traductor: si usa Windows, puede cambiarlo al comando de Windows, como Dir, y no lo repetiré más tarde. }); Si se produce un error, el primer parámetro será una instancia de la clase de error. Si el primer parámetro no contiene un error, el segundo parámetro stDout contendrá la salida estándar del comando. El último parámetro contiene salida de error relacionada con el comando. El listado 8-1 muestra un ejemplo más complejo de ejecución de comandos externos Listado 8-1: Ejecutar comandos externos (código fuente: capítulo8/01_external_command.js) La copia del código es la siguiente: // Importar la función ejecutiva del módulo child_process var exec = require ('child_process'). Exec; // llamando al comando "Cat *.js | wc -l" Exec ('Cat *.js | wc l', function (err, stdout, stderr) {// línea 4 // El comando sale o la llamada falla if (err) { // no se pudo iniciar un proceso externo console.log ('Child_process Exit, el código de error es:', err.code); devolver; } } En la cuarta línea, pasamos "Cat *.js | wc -l" como el primer parámetro en ejecutarse. También puede probar cualquier otro comando, siempre que el comando que haya usado en el shell esté bien. Luego tome una función de devolución de llamada como el segundo parámetro, que se llamará cuando ocurra un error o el proceso infantil finalice. También puede pasar un tercer parámetro opcional antes de la función de devolución de llamada, que contiene algunas opciones de configuración, como: La copia del código es la siguiente: var exec = require ('child_process'). Exec; opciones var = { Tiempo de espera: 1000, Killsignal: 'Sigkill' }; Exec ('Cat *.js | Wc l', Opciones, funciones (err, stdout, stderr) { // ... }); Los parámetros que se pueden usar son: 1.CWD: el directorio actual, puede especificar el directorio de trabajo actual. 2. Codificación: el formato de codificación del contenido de salida del proceso infantil, el valor predeterminado es "UTF8", es decir, codificación UTF-8. Si la salida del proceso infantil no es UTF8, puede usar este parámetro para configurarlo. Los formatos de codificación compatibles son: La copia del código es la siguiente: ascii UTF8 UCS2 base64 Si desea saber más sobre estos formatos de codificación compatibles con el nodo, consulte el Capítulo 4 "Usando el búfer para procesar, codificar y decodificar datos binarios". 1.TimeOut: el tiempo de espera de ejecución de comandos en milisegundos, el valor predeterminado es 0, es decir, no hay límite, y espere hasta que finalice el proceso infantil. 2.MaxBuffer: especifica el número máximo de bytes permitidos por la corriente Stdout y la corriente stderr. Si se alcanza el valor máximo, el proceso infantil será asesinado. El valor predeterminado es 200*1024. 3. Killsignal: la señal final enviada al proceso del niño cuando el tiempo de espera o el caché de salida alcanza el valor máximo. El valor predeterminado es "Sigterm", que enviará una señal de terminación al proceso infantil. Esto generalmente se usa para terminar el proceso de manera ordenada. Cuando se usa señales Sigterm, el proceso también puede procesar o reescribir el comportamiento predeterminado del procesador de señal después de recibirlo. Si el proceso de destino lo necesita, puede transmitir otras señales al mismo tiempo (como SigusR1). También puede optar por enviar una señal Sigkill, que será procesada por el sistema operativo y obligará al proceso infantil a terminar de inmediato, de modo que no se realice cualquier operación de limpieza del proceso infantil. Si desea controlar aún más el final del proceso, puede usar el comando child_process.spawn, que se introducirá más adelante. 1.EVN - Especifica la variable de entorno pasada al proceso infantil. El valor predeterminado es nulo, lo que significa que el proceso infantil heredará las variables de entorno de todos los procesos principales antes de que se cree. Nota: Usando la opción KillSignal, puede enviar señales al proceso de destino como una cadena. En el nodo, la señal existe como una cadena. Aquí hay una lista de señales UNIX y operaciones predeterminadas correspondientes: Es posible que desee proporcionar un conjunto de variables de entorno principal extensible para el proceso infantil. Si modifica directamente el objeto Process.env, cambiará las variables de entorno de todos los módulos en el proceso de nodo, lo que causará muchos problemas. La alternativa es crear un nuevo objeto y copiar todos los parámetros en Process.env, consulte el ejemplo 8-2: Listado 8-2: Use variables de entorno parametrizadas para ejecutar comandos (código fuente: capítulo8/02_env_vars_augment.js) La copia del código es la siguiente: var env = Process.env, Varname, envcopy = {}, exec = require ('Child_process'). Exec; // Copiar process.env a envcopy para (vaname en eV) { envcopy [varname] = env [varname]; } // Establecer algunas variables personalizadas Envcopy ['Custom env var1'] = 'algún valor'; Envcopy ['Custom env var2'] = 'algún otro valor'; // Ejecutar comandos usando Process.env y variables personalizadas Exec ('ls la', {env: envcopy}, function (err, stdout, stderr) { if (err) {lanzar err; } console.log ('stdout:', stdout); console.log ('stderr:', stderr); } En el ejemplo anterior, se crea una variable Envcopy para guardar las variables de entorno. Primero copia las variables de entorno del proceso de nodo desde Process.env, luego agrega o reemplaza algunas variables de entorno que deben modificarse, y finalmente pasa Envcopy como un parámetro variable de entorno a la función EXEC y ejecuta comandos externos. Recuerde que las variables de entorno se pasan entre procesos a través del sistema operativo, y todos los tipos de valores variables de entorno llegan al proceso infantil como cadenas. Por ejemplo, si el proceso principal toma el número 123 como una variable de entorno, el proceso infantil recibirá "123" como una cadena. En el siguiente ejemplo, se crearán dos scripts de nodos en el mismo directorio: Parent.js y Child.js. El primer script llamará al segundo. Creemos estos dos archivos: Listado 8-3: El proceso principal establece variables de entorno (capítulo8/03_environment_number_parent.js) La copia del código es la siguiente: var exec = require ('child_process'). Exec; Exec ('node child.js', {env: {número: 123}}, function (err, stdout, stderr) { if (err) {lanzar err; } console.log ('stdout:/n', stdout); console.log ('stderr:/n', stderr); }); Guarde este código en Parent.js. El siguiente es el código fuente del proceso infantil y guárdelos a Child.js (ver Ejemplo 8-4) Ejemplo 8-4: Variables de entorno analizador de subprocesos (capítulo8/04_environment_number_child.js) La copia del código es la siguiente: VAR Número = Process.env.number; console.log (typeof (número)); // → "cadena" número = parseint (número, 10); console.log (typeof (número)); // → "número" Después de guardar este archivo como child.js, puede ejecutar el siguiente comando en este directorio: La copia del código es la siguiente: $ node parent.js Verá la siguiente salida: La copia del código es la siguiente: Sdtou: cadena número stderr: Como puede ver, aunque el proceso principal pasa una variable de entorno numérico, el proceso infantil lo recibe como una cadena (ver la segunda línea de salida), y en la tercera línea analiza la cadena en un número. Generar el proceso infantil Como puede ver, puede usar la función child_process.exec () para iniciar un proceso externo y llamar a su función de devolución de llamada al final del proceso. Esto es muy simple de usar, pero hay algunas desventajas: 1. Además de usar parámetros de línea de comandos y variables de entorno, Exec () no puede comunicarse con los procesos infantiles. 2. La salida del proceso infantil se almacena en caché, por lo que no puede transmitirlo, puede quedarse sin memoria Afortunadamente, el módulo Child_Process de Node permite que la granularidad más fina controle el inicio, la parada y otras operaciones convencionales de procesos infantiles. Puede iniciar un nuevo proceso infantil en la aplicación. Node proporciona un canal de comunicación bidireccional, lo que permite que los procesos de padres e hijos se envíen y reciban datos de cadena entre sí. El proceso principal también puede tener algunas operaciones de gestión para el proceso infantil, enviar señales al proceso infantil y cerrar con fuerza el proceso del niño. Crear un proceso infantil Puede usar la función child_process.spawn para crear un nuevo proceso infantil, vea el ejemplo 8-5: Ejemplo 8-5: Generar el proceso infantil. (Capítulo8/05_Spawning_Child.js) La copia del código es la siguiente: // Importar la función de desove del módulo child_process var Spawn = require ('Child_process'). Spawn; // Generar procesos infantiles utilizados para ejecutar el comando "Tail -f /var/log/system.log" var child = spawn ('cola', ['-f', '/var/log/system.log']); El código anterior genera un proceso infantil utilizado para ejecutar comandos de cola y toma "-f" y "/bar/log/system.log" como parámetros. El comando Tail monitoreará el archivo /var/log/system.og (si está presente), y luego emitirá todos los nuevos datos adjuntos a la transmisión de salida estándar STDOut. La función de desove devuelve un objeto de proceso infantil, que es un objeto de puntero, que encapsula la interfaz de acceso del proceso real. En este ejemplo, asignamos este nuevo descriptor a una variable llamada Child. Escuche los datos de los procesos infantiles Cualquier mango de proceso infantil que contenga el atributo STDOUT tomará la salida estándar del proceso infantil como un objeto de flujo. Puede vincular el evento de datos en este objeto de transmisión, de modo que cada vez que esté disponible un bloque de datos, se llame a la función de devolución de llamada correspondiente, consulte el siguiente ejemplo: La copia del código es la siguiente: // imprima la salida del proceso infantil en la consola child.stdout.on ('data', function (data) { console.log ('salida de cola:' + datos); }); Cada vez que el proceso del niño emite datos a la salida de salida estándar, el proceso principal se notifica e imprime los datos en la consola. Además de la salida estándar, el proceso tiene otra secuencia de salida predeterminada: la secuencia de error estándar, que generalmente se usa para salir de información de error. En este ejemplo, si el archivo /var/log/system.log no existe, el proceso de cola generará un mensaje similar al siguiente: "/var/log/system.log: no hay dicho archivo o directorio". Al escuchar la transmisión Stderr, el proceso principal se notificará cuando ocurra este error. El proceso principal puede escuchar transmisiones de error estándar como esta: La copia del código es la siguiente: child.stderr.on ('data', function (data) { console.log ('salida de error de cola:', datos); }); La propiedad Stderr, como Stdout, también es una transmisión de solo lectura. Cada vez que un proceso de hijo genera datos en la secuencia de errores estándar, el proceso principal será notificado y datos de salida. Enviar datos al proceso infantil Además de recibir datos de la secuencia de salida del proceso infantil, el proceso principal también puede escribir datos en la entrada estándar del proceso infantil a través de la propiedad childpoces.stdin para enviar datos al proceso infantil. El proceso infantil puede escuchar los datos de entrada estándar a través de la transmisión de solo lectura de proceso, pero tenga cuidado de que primero reanude el flujo de entrada estándar, ya que está en un estado pausado de forma predeterminada. El ejemplo 8-6 creará un programa que contiene las siguientes funciones: 1.+1 Aplicación: una aplicación simple que puede recibir enteros de la entrada estándar, luego agregarlos y luego emitir el resultado después de la adición a la secuencia de salida estándar. Como un servicio informático simple, esta aplicación simula el proceso de nodo como un servicio externo que puede realizar tareas específicas. 2. Pruebe el cliente de la aplicación +1, envíe un entero aleatorio y luego genere el resultado. Se utiliza para demostrar cómo el proceso de nodo genera un proceso infantil y luego le permite realizar tareas específicas. Use el siguiente código en el Ejemplo 8-6 para crear un archivo llamado Plus_One.js: Ejemplo 8-6: +1 Aplicación (Capítulo8/06_plus_one.js) La copia del código es la siguiente: // restaurar la secuencia de entrada estándar que se detiene de forma predeterminada process.stdin.resume (); process.stdin.on ('data', function (data) { número var; intentar { // analizar los datos de entrada en un entero número = parseInt (data.ToString (), 10); // +1 número += 1; // resultado de salida process.stdout.write (número + "/n"); } catch (err) { process.stderr.write (err.message + "/n"); } }); En el código anterior, esperamos los datos de la secuencia de entrada estándar de Stdin. Siempre que los datos estén disponibles, suponemos que es un entero y lo analizamos en una variable entera, luego agregamos 1 y generamos el resultado a la secuencia de salida estándar. Puede ejecutar este programa a través del siguiente comando: La copia del código es la siguiente: $ node plus_one.js Después de ejecutar, el programa comienza a esperar la entrada. Si ingresa a un entero y presiona Entrar, verá un número después de que se agregue 1 para mostrar en la pantalla. Puede salir del programa presionando CTRL-C. Un cliente de prueba Ahora desea crear un proceso de nodo para usar los servicios informáticos proporcionados por la "aplicación +1" anterior. Primero cree un archivo llamado plus_one_test.js, vea el ejemplo 8-7: Ejemplo 8-7: Aplicación Test +1 (Capítulo8/07_Plus_One_test.js) La copia del código es la siguiente: var Spawn = require ('Child_process'). Spawn; // Generar un proceso infantil para ejecutar la aplicación +1 var child = spawn ('nodo', ['plus_one.js']); // llama a la función cada segundo setInterval (function () { // crear un número aleatorio menor de 10.000 Var número = Math.floor (Math.random () * 10000); // Envía ese número al proceso del niño: child.stdin.write (número + "/n"); // Obtenga la respuesta del proceso infantil e imprima: child.stdout.once ('data', function (data) { console.log ('Child respondió a' + número + 'con:' + data); }); }, 1000); child.stderr.on ('data', function (data) { process.stdout.write (datos); }); Desde la primera línea hasta la cuarta línea, se inicia un proceso infantil para ejecutar la "aplicación +1", y luego las siguientes operaciones se realizan cada segundo utilizando la función SetInterval: 1. Cree un nuevo número aleatorio de menos de 10000 2. Pase este número como una cadena al proceso infantil 3. Espere a que el proceso infantil responda a una cadena 4. Debido a que desea recibir solo 1 número a la vez, debe usar child.stdout.once en lugar de child.stdout.on. Si se utiliza este último, se registrará una función de devolución de llamada de un evento de datos cada 1 segundo. Cada función de devolución de llamada registrada se ejecutará cuando el STDOUT del proceso infantil reciba datos. De esta manera, encontrará que el mismo resultado de cálculo se emitirá varias veces. Este comportamiento obviamente es incorrecto. Recibir notificaciones cuando el proceso infantil salga Cuando salga el proceso infantil, el evento de salida será despedido. El ejemplo 8-8 muestra cómo escucharlo: Ejemplo 8-8: Escuche el evento de salida del proceso infantil (capítulo8/09_listen_child_exit.js) La copia del código es la siguiente: var Spawn = require ('Child_process'). Spawn; // Generar el proceso infantil para ejecutar el comando "ls -la" var child = spawn ('ls', ['-la']); child.stdout.on ('data', function (data) { console.log ('Datos del niño:' + datos); }); // Cuando el proceso infantil sale: <strong> child.on ('exit', function (código) { console.log ('Proceso infantil terminado con código' + código); }); </strong> En las últimas líneas de Black Code, el proceso principal utiliza el evento de salida del proceso infantil para escuchar su evento de salida. Cuando ocurre el evento, la consola muestra la salida correspondiente. El código de salida del proceso infantil se pasará a la función de devolución de llamada como el primer parámetro. Algunos programas usan un código de salida sin 0 para representar un cierto estado de falla. Por ejemplo, si intenta ejecutar el comando "ls al hacer clic en filename.txt", pero el directorio actual no tiene este archivo, obtendrá un código de salida con un valor de 1, consulte el ejemplo 8-9: Ejemplo 8-9: obtenga el código de salida del proceso infantil (capítulo 8/10_child_exit_code.js) La copia del código es la siguiente: var Spawn = require ('Child_process'). Spawn; // Generar el proceso infantil y ejecutar el comando "ls does_not_exist.txt" var child = spawn ('ls', ['does_not_exist.txt']); // Cuando el proceso infantil sale Child.on ('Exit', function (Code) { console.log ('Proceso infantil terminado con código' + código); }); En este ejemplo, el evento de salida desencadena la función de devolución de llamada y pasa el código de salida del proceso infantil como el primer parámetro. Si el proceso infantil sale de manera anormal debido a ser asesinado por una señal, el código de señal correspondiente se pasará a la función de devolución de llamada como un segundo parámetro, como en el ejemplo 8-10: Listado 8-10: Obtenga la señal de salida del proceso infantil (Capítulo8/11_Child_exit_Signal.js) La copia del código es la siguiente: var Spawn = require ('Child_process'). Spawn; // Generar el proceso infantil y ejecutar el comando "Sleep 10" var child = spawn ('dormir', ['10']); setTimeOut (function () { child.kill (); }, 1000); Child.on ('Exit', Function (Code, Signal) { if (código) { console.log ('Proceso infantil terminado con código' + código); } else if (señal) { console.log ('Proceso infantil terminado debido a la señal' + señal); } }); En este ejemplo, un proceso infantil se inicia para realizar el sueño 10 segundos, pero se envía una señal Sigkill al proceso infantil antes de 10 segundos, lo que dará como resultado la siguiente salida: La copia del código es la siguiente: El proceso infantil terminó debido a la señal sigter Enviar una señal y matar el proceso En esta sección, aprenderá cómo usar señales para administrar subprocesos. Las señales son una manera fácil para que un proceso de los padres se comunique con los niños e incluso mate a los niños. Los diferentes códigos de señal representan diferentes significados, y hay muchas señales, algunas de las cuales son las más comunes utilizadas para matar procesos. Si un proceso recibe una señal que no sabe cómo manejar, el programa se interrumpirá por excepción. Algunas señales son procesadas por subprocesos, mientras que otras solo pueden ser procesadas por el sistema operativo. En general, puede usar el método infantil. La copia del código es la siguiente: var Spawn = require ('Child_process'). Spawn; var child = spawn ('dormir', ['10']); setTimeOut (function () { child.kill (); }, 1000); También puede enviar una señal específica pasando en una cadena que identifica la señal como el único parámetro del método Kill: La copia del código es la siguiente: child.kill ('Sigusr2'); Cabe señalar que aunque el nombre de este método es matar, la señal enviada no necesariamente mata el proceso infantil. Si el niño procesó la señal, se sobrescribió el comportamiento de señal predeterminado. Los subprocesos escritos en el nodo pueden reescribir la definición del procesador de señal como el siguiente: La copia del código es la siguiente: Process.on ('SigUSR2', function () { console.log ('Got a Sigusr2 Signal'); }); Ahora, ha definido el procesador de señal SIGUSR2. Cuando su proceso recibe nuevamente la señal SIGUSR2, no será asesinada, sino que emite la oración "obtuvo una señal SIGUSR2". Usando este mecanismo, puede diseñar una forma simple de comunicarse con el proceso infantil o incluso ordenarlo. Aunque no es tan rico como el uso de la entrada estándar, este método es mucho más simple. resumen En este capítulo, aprendimos a usar el método child_process.exec para ejecutar comandos externos. De esta manera, podemos pasar parámetros al proceso infantil en lugar de usar parámetros de línea de comando, pero en su lugar definir variables de entorno. También aprendí a generar procesos infantiles llamando al método child_process.spawn para llamar a comandos externos. De esta manera, puede usar flujos de entrada y flujos de salida para comunicarse con procesos infantiles, o usar señales para comunicarse con procesos infantiles y matar procesos.