Los amigos que están familiarizados con js saben que js 是单线程
. En Node, se adopta un modelo de subproceso único multiproceso . Debido a la limitación de un solo subproceso de JavaScript, en servidores de múltiples núcleos, a menudo necesitamos iniciar múltiples procesos para maximizar el rendimiento del servidor.
Los clústeres de procesos de Node.js se pueden utilizar para ejecutar múltiples instancias de Node.js, que pueden distribuir la carga de trabajo entre los subprocesos de su aplicación. Cuando no sea necesario aislar el proceso, utilice el módulo worker_threads
, que permite ejecutar varios subprocesos de aplicación dentro de una única instancia de Node.js.
Node introdujo el módulo de clúster después de la versión V0.8,一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
El módulo de clúster facilita la creación de procesos secundarios que comparten puertos de servidor.
La capa inferior del clúster es el módulo child_process. Además de enviar mensajes normales, también puede enviar objetos subyacentes
TCP
,UDP
, etc.cluster
es una aplicación combinada del módulochild_process
y el módulonet
. Cuando se inicia el clúster, el servidor TCP se iniciará internamente y el descriptor de archivo del socket del servidor TCP se enviará al proceso de trabajo.
En la aplicación del módulo cluster
,一个主进程只能管理一组工作进程
. Su modo de funcionamiento no es tan flexible como el módulo child_process
, pero es más estable:
const cluster = require('cluster') y
.isMaster
identifica el proceso principal, Node<16.isPrimary.isPrimary
el proceso principal, Node>16.isWorker.isWorker
el subproceso. .worker es.worker
del trabajo actual Referencia al objeto de proceso [en el proceso secundario].workers
almacena el hash del objeto de proceso de trabajo activo, con el campo id
como clave. Esto facilita el recorrido por todos los procesos de los trabajadores. Sólo está disponible en el proceso principal. cluster.wokers[id] === worker
[en el proceso principal].settings
es un elemento de configuración del clúster de solo lectura. Después de llamar al método .setupPrimary() o .fork(), este objeto de configuración contendrá la configuración, incluidos los valores predeterminados. Anteriormente un objeto vacío. Este objeto no debe cambiarse ni configurarse manualmente.cluster.settings
:- `execArgv` <string[]>Una lista de parámetros de cadena pasados al archivo ejecutable de Node.js. **Predeterminado:** `process.execArgv`. - `exec` <cadena> Ruta del archivo al archivo del proceso de trabajo. **Predeterminado:** `proceso.argv[1]`. - `args` <string[]> Argumentos de cadena pasados al proceso de trabajo. **Predeterminado:** `process.argv.slice(2)`. - `cwd` <cadena>El directorio de trabajo actual del proceso de trabajo. **Predeterminado:** `indefinido` (heredado del proceso principal). - `serialización` <cadena>Especifica el tipo de serialización utilizado para enviar mensajes entre procesos. Los valores posibles son `'json'` y `'avanzado'`. **Predeterminado:** `falso`. - `silent` <boolean>Si se envía la salida a la entrada y salida estándar del proceso principal. **Predeterminado:** `falso`. - `stdio` <Array> configura la entrada y salida estándar del proceso generado. Debido a que el módulo del clúster depende de IPC para ejecutarse, esta configuración debe contener una entrada `'ipc'`. Cuando se proporciona esta opción, anula "silencioso". - `uid` <número> establece el ID de usuario del proceso. - `gid` <número> establece el ID de grupo del proceso. - `inspectPort` <número> | <Función> Establece el puerto del inspector para el proceso de trabajo. Puede ser un número o una función que no toma parámetros y devuelve un número. De forma predeterminada, cada proceso de trabajo tiene su propio puerto, comenzando desde `process.debugPort` del proceso principal y aumentando. - `windowsHide` <boolean> Oculta la ventana de consola del proceso generado que normalmente se crea en sistemas Windows. **Predeterminado:** `falso`.
.fork([env])
genera un nuevo proceso de trabajo [en el proceso principal].setupPrimary([settings])
Node>16.setupMaster([settings])
se usa para cambiar el comportamiento predeterminado de 'fork', después del uso La configuración aparecerá en cluster.settings
. Cualquier cambio de configuración solo afectará las llamadas futuras a .fork()
, no los procesos de trabajo que ya se estén ejecutando. Los valores predeterminados anteriores solo se aplican a la primera llamada. El nodo es menor que 16 [En el proceso principal].disconnect([callback])
Se llama cuando todos los procesos de trabajo se desconectan y cierran identificadores [En el proceso principal]Para que el clúster sea más estable y robusto, cluster
también expone muchos eventos:
'message'
, que se activan cuando el proceso maestro del clúster recibe un mensaje de cualquier proceso de trabajo.'exit'
, cuando cualquier proceso de trabajo muere, el módulo del clúster activará el evento 'exit'
.cluster.on('salida', (trabajador, código, señal) => { console.log('el trabajador %d murió (%s). reiniciando...', trabajador.proceso.pid, señal || código); cluster.fork(); });
'listening'
, después de llamar listen()
desde el proceso de trabajo, cuando el evento 'listening'
se activa en el servidor, cluster
en el proceso principal también activará el evento 'listening'
.cluster.on('escuchando', (trabajador, dirección) => { consola.log( `Un trabajador ahora está conectado a ${address.address}:${address.port}`); });
'fork'
, cuando se genera un nuevo proceso de trabajo, el módulo del clúster activará el evento 'fork'
.cluster.on('tenedor', (trabajador) => { tiempos de espera[trabajador.id] = setTimeout(errorMsg, 2000); });
'setup'
, que se activa cada vez que se llama a .setupPrimary()
.disconnect
se activa después de que se desconecta el canal IPC del proceso de trabajo. Cuando el proceso de trabajo sale normalmente, se cierra o se desconecta manualmentecluster.on('disconnect', (worker) => { console.log(`El trabajador #${worker.id} se ha desconectado`); });
El objeto Worker
contiene toda la información pública y los métodos del proceso de trabajo. En el proceso principal, puedes usar cluster.workers
para obtenerlo. En un proceso de trabajo, puede utilizar cluster.worker
para obtenerlo.
.id
. Cada nuevo proceso de trabajo recibe su propia identificación única. Esta identificación se almacena en id
. Cuando un proceso de trabajo está activo, esta es la clave que lo indexa en cluster.workers
..process
Todos los procesos de trabajo se crean usando child_process.fork()
y el objeto devuelto por esta función se almacena como .process
. En el proceso de trabajo, se almacena el process
global..send(message[, sendHandle[, options]][, callback])
envía un mensaje al proceso de trabajo o al proceso principal, y puede optar por utilizar un identificador. En el proceso principal, esto envía un mensaje a un proceso de trabajo específico. Es lo mismo que ChildProcess.send()
. En el proceso de trabajo, esto envía un mensaje al proceso principal. Es lo mismo que process.send()
..destroy()
.kill([signal])
Esta función eliminará el proceso de trabajo. La función kill()
finaliza el proceso de trabajo sin esperar una desconexión elegante; tiene el mismo comportamiento que worker.process.kill()
. Para compatibilidad con versiones anteriores, este método tiene el alias de worker.destroy()
..disconnect([callback])
se envía al proceso de trabajo, lo que hace que llame a su propio .disconnect()
que cerrará todos los servidores, esperará eventos 'close'
en esos servidores y luego desconectará el canal IPC..isConnect()
Esta función devuelve true
si el proceso de trabajo está conectado a su proceso principal a través de su canal IPC, false
en caso contrario. Los procesos de trabajo se conectan a su proceso maestro después de la creación..isDead()
Esta función devuelve true
si el proceso de trabajo ha terminado (debido a la salida o la recepción de una señal). De lo contrario, devuelve false
.Para hacer que el clúster sea más estable y robusto, el módulo cluster
también expone muchos eventos:
'message'
, en el proceso de trabajo.cluster.workers[id].on('message', messageHandler);
'exit'
, cuando cualquier proceso de trabajo muere,当前worker工作进程
activará el evento 'exit'
.si (cluster.isPrimary) { trabajador constante = cluster.fork(); trabajador.on('salir', (código, señal) => { si (señal) { console.log(`el trabajador fue asesinado por la señal: ${signal}`); } más si (código! == 0) { console.log(`el trabajador salió con el código de error: ${code}`); } demás { console.log('¡éxito del trabajador!'); } }); }
'listening'
, llame listen()
desde el proceso de trabajo para escuchar el proceso de trabajo actual.cluster.fork().on('escuchando', (dirección) => { // El proceso de trabajo está escuchando });
disconnect
, que se activa después de que se desconecta el canal IPC del proceso de trabajo. Cuando el proceso de trabajo sale normalmente, se cancela o se desconecta manualmentecluster.fork().on('disconnect', () => { // Limitado a activarse en el objeto de trabajo actual});
En Node, la comunicación entre procesos (IPC) se utiliza para realizar la comunicación entre procesos entre el proceso principal y los subprocesos. .send()
(a.send significa enviar un mensaje a Send) para enviar mensajes y escuchar eventos message
para recopilar información. Esto lo implementa cluster模块
integrando EventEmitter
. También es un ejemplo simple de comunicación entre procesos en el sitio web oficial
process.on('message')
, process.send()
child.on('message')
, child.send()
# cluster.isMaster # cluster.fork() # cluster.trabajadores # cluster.workers[id].on('mensaje', messageHandler); # cluster.workers[id].send(); # proceso.on('mensaje', messageHandler); # proceso.enviar(); const clúster = requerir('clúster'); const http = requerir('http'); # Proceso principal si (cluster.isMaster) { // Realizar un seguimiento de las solicitudes http console.log(`El ${process.pid} principal se está ejecutando`); sea numReqs = 0; // Contar solicitudes función manejador de mensajes (mensaje) { si (msg.cmd && msg.cmd === 'notificarRequest') { numReqs += 1; } } // Iniciar trabajadores y escuchar mensajes que contengan notifyRequest // Iniciar multiproceso (número de núcleos de CPU) // Generar proceso de trabajo. const numCPUs = require('os').cpus().length; for (sea i = 0; i < numCPU; i++) { consola.log(i) cluster.fork(); } // el proceso principal del trabajador del clúster se comunica con los procesos secundarios para (const id en cluster.workers) { // ***Escuche eventos de procesos secundarios cluster.workers[id].on('message', messageHandler); // ***Enviar cluster.workers[id].send({ al proceso hijo tipo: 'masterToWorker', de: 'maestro', datos: { número: Math.floor(Math.random() * 50) } }); } cluster.on('salida', (trabajador, código, señal) => { console.log(`el trabajador ${worker.process.pid} murió`); }); } demás { # Procesos secundarios // Los procesos de trabajo pueden compartir cualquier conexión TCP // En este ejemplo, es un servidor HTTP // Los procesos de trabajo tienen un servidor http. http.Servidor((solicitud, res) => { res.writeHead(200); res.end('hola mundon'); //******! ! ! ! ¡Notifique al maestro sobre la solicitud! ! ! ! ! ! ******* //****** Enviar proceso.send({ cmd: 'notifyRequest' }); //****** Escuche proceso.on('mensaje', función(mensaje) { // xxxxxxx }) }).escuchar(8000); console.log(`Trabajador ${process.pid} iniciado`); }
La comunicación entre procesos de NodeJS solo implica el paso de mensajes y, en realidad, no transfiere objetos.
Antes de enviar el mensaje, el método send()
ensamblará el mensaje en un identificador y un mensaje. Este mensaje será serializado por JSON.stringify
, es decir, al pasar el identificador, no se pasará el objeto completo. transmitidos a través del canal IPC. Todas son cadenas y se restauran en objetos a través de JSON.parse
después de la transmisión.
Hay app.listen(port)
en el código al bifurcar, ¿por qué varios procesos pueden escuchar el mismo puerto?
La razón es que el proceso principal envía el identificador de un objeto de servicio que pertenece al proceso principal a múltiples subprocesos a través del método send(), por lo que para cada subproceso, después de restaurar el identificador, obtienen el mismo objeto de servicio. Cuando la red Cuando se realiza una solicitud al servidor, el servicio de proceso es preventivo, por lo que no se producirá ninguna excepción al escuchar en el mismo puerto.
# master.js const fork = require('child_process').fork; const cpus = requerir('os').cpus(); for (let i=0; i<cpus.length; i++) { trabajador constante = fork('trabajador.js'); console.log('proceso de trabajo creado, pid: %s ppid: %s', trabajador.pid, proceso.pid); }
# trabajador.js const http = requerir('http'); http.createServer((solicitud, res) => { res.end('Soy trabajador, pid: ' + proceso.pid + ', ppid: ' + proceso.ppid); }).listen(3000);
en el ejemplo de código anterior, cuando la consola ejecuta
node master.js
solo un trabajador puede escuchar el puerto 3000 y el resto arrojaráError: listen EADDRINUSE :::3000
error.
发送句柄
entre procesos después de la versión v0.5.9/**. * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * mensaje * enviar mango */ subprocess.send (mensaje, sendHandle)
Una vez establecido el canal IPC entre los procesos padre e hijo, el mensaje se envía a través del método de envío del objeto de subproceso.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
, para resolver el problema de ocupación de puerto multiproceso anterior, pasamos el socket del proceso principal al proceso secundario.
# maestro.js const fork = require('child_process').fork; const cpus = requerir('os').cpus(); servidor constante = require('net').createServer(); servidor.escuchar(3000); proceso.título = 'nodo-maestro' for (let i=0; i<cpus.length; i++) { trabajador constante = fork('trabajador.js'); # Pasar el identificador trabajador.send('servidor', servidor); console.log('proceso de trabajo creado, pid: %s ppid: %s', trabajador.pid, proceso.pid); }
// trabajador.js dejar trabajador; proceso.título = 'trabajador-nodo' proceso.on('mensaje', función (mensaje, sendHandle) { si (mensaje === 'servidor') { trabajador = enviarMango; trabajador.on('conexión', función (zócalo) { console.log('Soy trabajador, pid: ' + proceso.pid + ', ppid: ' + proceso.ppid) }); } });
Verifique que la consola ejecute node master.js
Si comprende cluster
, sabrá que los procesos secundarios se crean a través de cluster.fork()
. En Linux, el sistema proporciona de forma nativa el método fork
, entonces, ¿por qué Node elige implementar cluster模块
por sí mismo en lugar de utilizar directamente el método nativo del sistema? Las razones principales son los dos puntos siguientes:
El proceso de bifurcación monitorea el mismo puerto, lo que provocará errores de ocupación del puerto.
No hay equilibrio de carga entre los procesos de bifurcación, lo que puede provocar fácilmente el fenómeno de rebaño atronador
en cluster模块
. El primer problema, determinamos si el proceso actual es master进程
; si lo es, escucha en el puerto. De lo contrario, se representa como un worker进程
bifurcado y no escucha en el puerto.
En respuesta a la segunda pregunta, cluster模块
tiene una función de equilibrio de carga incorporada. master进程
es responsable de escuchar el puerto para recibir solicitudes y luego asignarlas a los worker进程
correspondientes a través del algoritmo de programación (el valor predeterminado es). Round-Robin, el algoritmo de programación se puede modificar a través de la variable de entorno NODE_CLUSTER_SCHED_POLICY
).
Cuando el código genera una excepción que no se detecta, el proceso se cerrará. En este momento, Node.js proporciona process.on('uncaughtException', handler)
para detectarla, pero cuando se produce una excepción. Cuando el proceso de trabajo encuentra una excepción no detectada, ya se encuentra en un estado incierto. En este momento, debemos dejar que el proceso salga correctamente:
+---------+ +---------+ | Trabajador | | +---------+ +----+----+ | Excepción no detectada | +----------------+ | | | Trabajador <----------+ | | +----+----+ | desconectar | bifurcar a un nuevo trabajador | +-----------------------> + ----------------------- -> | | espera... | | salir | +-----------------------> | | morir | | | |
Cuando un proceso tiene una excepción que causa un bloqueo o OOM y es eliminado por el sistema, a diferencia de cuando ocurre una excepción no detectada, todavía tenemos la oportunidad de dejar que el proceso continúe ejecutándose. el proceso actual sale directamente y el Maestro inmediatamente bifurca un nuevo trabajador.
El módulo child_process proporciona la capacidad de derivar procesos hijo, que es simplemente执行cmd命令的能力
. De forma predeterminada, stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. Estos oleoductos tienen una capacidad limitada (y específica de la plataforma). Si el proceso secundario excede este límite al escribir en stdout y no se captura ningún resultado, el proceso secundario se bloquea y espera a que el búfer de canalización acepte más datos. Este es el mismo comportamiento que una tubería en el caparazón. Si la salida no se consume, use la opción { stdio: 'ignore' }.
const cp = require('child_process');
El proceso hijo creado a través de API no tiene conexión necesaria con el proceso padre.
Se utilizan 4 métodos asincrónicos para crear procesos hijo: fork, exec, execFile, spawn
Node.
fork(modulePath, args)
: se utiliza cuando desea ejecutar un proceso de Nodo como un proceso independiente, de modo que el procesamiento de cálculo y el descriptor de archivo estén separados del proceso principal de Nodo (copiando un proceso secundario)sin Nodo
spawn(command, args)
: Procese algunos problemas. Utilice execFile(file, args[, callback]) cuando haya muchas E/S de subprocesos o cuando el proceso tenga una gran cantidad de resultadosexecFile(file, args[, callback])
Úselo cuando solo necesite ejecutar un programa externo. La velocidad de ejecución es rápida y es relativamente seguro procesar la entrada del usuario.exec(command, options)
: se utiliza cuando desea acceder directamente al comando de shell del hilo. Asegúrese de prestar atención alos tres métodos de sincronización ingresados por el usuario: execSync
. execFileSync
, spawnSync
Los otros tres métodos son extensiones de spawn()
.
. Recuerde, el proceso secundario de Node.js derivado es independiente. El proceso padre, excepto el canal de comunicación IPC establecido entre ambos. Cada proceso tiene su propia memoria y su propia instancia V8
.
Por ejemplo, cree dos archivos, trabajador.js y master.js, en un directorio:
# child.js. const t = JSON.parse(proceso.argv[2]); console.error(`proceso hijo t=${JSON.stringify(t)}`); Process.send({hola:`son pid=${process.pid} por favor dale a papá Process pid=${process.ppid} hola`}); proceso.on('mensaje', (msg)=>{ console.error(`proceso hijo msg=${JSON.stringify(msg)}`); });
#padre.js const {fork} = require('child_process'); para(sea i = 0; i < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,nombre:1})]); p.on('mensaje', (mensaje) => { console.log(`messsgae del mensaje secundario=${JSON.stringify(msg)}`,); }); p.send({hola:`Saludos desde papá ${process.pid} id del proceso=${i}`}); }
Inicie parent.js a través node parent.js
y luego verifique la cantidad de procesos a través de ps aux | grep worker.js
Podemos encontrar que, idealmente, la cantidad de procesos es igual a la cantidad de núcleos de CPU y cada proceso usa uno. Núcleo de la CPU.
Este es el modo clásico Maestro-Trabajador (modo maestro-esclavo)
De hecho, bifurcar un proceso es costoso y el propósito de copiar un proceso es aprovechar al máximo los recursos de la CPU, por lo que NodeJS utiliza un enfoque basado en eventos en un solo subproceso para resolver el problema de la alta concurrencia.
Escenarios aplicables <br/>Generalmente se utilizan para escenarios que requieren mucho tiempo y se implementan mediante nodos, como la descarga de archivos;
Fork puede implementar la descarga de subprocesos múltiples: divida el archivo en varios bloques, luego cada proceso descarga una parte y finalmente los junta
const cp = require('child_process'); // El primer parámetro es el nombre o ruta del archivo ejecutable que se ejecutará. aquí está el eco cp.execFile('echo', ['hola', 'mundo'], (err, stdout, stderr) => { si (err) { consola.error(err }); console.log('stdout: ', salida estándar); console.log('stderr: ', stderr); });
escenarios aplicables <br/> Más adecuado para tareas con poca sobrecarga y más atención a los resultados, como ls, etc.;
se usa principalmente para ejecutar un método de shell, y spawn es Todavía se llama internamente, pero hay un límite máximo de caché.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | ordenar | uniq`, (err, stdout, stderr) => { console.log(salida estándar); });
escenarios aplicables <br/>Más adecuado para tareas con poca sobrecarga y que prestan más atención a los resultados, como ls, etc.
tarea única
de interfaz de flujo de E/Sconst cp = require('child_process'); const child = cp.spawn('echo', ['hola', 'mundo']); child.on('error', consola.error); # La salida es una secuencia, salida al proceso principal stdout, la consola child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
Concatenación multitarea
const cp = require('child_process'); ruta constante = requerir('ruta'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); ordenación constante = cp.spawn('ordenar'); const uniq = cp.spawn('uniq'); #La salida es una secuencia cat.stdout.pipe(sort.stdin); ordenar.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout);
escenarios aplicables
spawn se transmite, por lo que es adecuado para tareas que requieren mucho tiempo, como ejecutar npm install e imprimir el proceso de instalación.
se activa después de que el proceso ha finalizado y el flujo de entrada y salida estándar (sdtio) del El proceso secundario se ha 'close'
. Este evento es diferente de exit
porque varios procesos pueden compartir la misma secuencia estándar.
Parámetros:
Pregunta: ¿El código tiene que existir?
(Parece que no por los comentarios sobre el código) Por ejemplo, si usa kill
para matar el proceso hijo, ¿cuál es el código?
parámetros de salida:
código, señal, si el proceso hijo sale solo, entonces code
es el código de salida; de lo contrario, es nulo;
Si el proceso hijo finaliza mediante una señal, entonces signal
es la señal para finalizar el proceso; de lo contrario, es nula.
De los dos, uno no debe ser nulo.
Cosas a tener en cuenta :
Cuando se activa el evento exit
, es posible que la secuencia stdio del proceso secundario aún esté abierta. (¿Escenario?) Además, nodejs escucha las señales SIGINT y SIGTERM, es decir, cuando nodejs recibe estas dos señales, no saldrá inmediatamente, sino que primero realizará un trabajo de limpieza y luego volverá a emitir estas dos señales. (Visualmente, js puede realizar trabajos de limpieza en este momento, como cerrar la base de datos, etc.)
SIGINT
: interrupción, señal de finalización del programa, generalmente emitida cuando el usuario presiona CTRL + C, utilizada para notificar al proceso en primer plano que finalice el proceso.
SIGTERM
: terminar, señal de fin del programa, esta señal se puede bloquear y procesar, y generalmente se usa para requerir que el programa salga normalmente. El comando de shell kill genera esta señal de forma predeterminada. Si no se puede terminar la señal, intentaremos SIGKILL (terminación forzada).
Cuando suceda lo siguiente, se activará un error. Cuando se activa el error, la salida puede activarse o no. (El corazón está roto)
se activa cuando se utiliza process.send()
para enviar un mensaje.
parámetro :
message
, es un objeto json o un valor primitivo sendHandle
, un objeto net.Socket o un objeto net.Server (los estudiantes familiarizados con cluster deberían estar familiarizados con esto)
: cuando llame .disconnected()
, configúrelo; a falso. Representa si puede recibir mensajes del proceso hijo o enviar mensajes al proceso hijo.
.disconnect() : cierra el canal IPC entre el proceso padre y el proceso hijo. Cuando se llama a este método, se activará el evento disconnect
. Si el proceso hijo es una instancia de nodo (creada a través de child_process.fork()), entonces también se puede llamar activamente a process.disconnect()
dentro del proceso hijo para terminar el canal IPC.
responde a problemas de subproceso único. Los métodos de múltiples procesos generalmente se usan para simular subprocesos múltiples.
están ocupados por el proceso del nodo. El núcleo del nodo de 7 subprocesos
es el motor v8. Una vez iniciado el nodo, se creará una instancia de v8. Esta instancia es el
del
单线程
, pero el entorno de host de Javascript, ya sea Node o el navegador, es de subprocesos múltiples.
¿Por qué Javascript es de un solo subproceso?
Este problema debe comenzar con el navegador. Para las operaciones DOM en el entorno del navegador, imagínese si varios subprocesos operan en el mismo DOM, será caótico, lo que significa que las operaciones DOM solo se pueden realizar de una manera. Evite conflictos de representación DOM. En el entorno del navegador, el hilo de representación de la interfaz de usuario y el motor de ejecución JS se excluyen mutuamente. Cuando se ejecuta uno, el otro se suspenderá.
Process.env.UV_THREADPOOL_SIZE = 644.
worker_threads
para proporcionar capacidades reales de subprocesos múltiples a Node.const {. es hilo principal, puerto principal, datos del trabajador, ID de hilo, canal de mensajes, puerto de mensajes, Obrero } = requerir('worker_threads'); función hilo principal() { para (sea i = 0; i < 5; i++) { trabajador constante = nuevo trabajador (__nombre de archivo, {datos del trabajador: i}); trabajador.on('exit', code => { console.log(`main: el trabajador se detuvo con el código de salida ${code}`); }); trabajador.on('mensaje', mensaje => { console.log(`principal: recibir ${msg}`); trabajador.postMessage(msj + 1); }); } } función hilotrabajador() { console.log(`trabajador: fechadeltrabajador ${datosdeltrabajador}`); parentPort.on('mensaje', mensaje => { console.log(`trabajador: recibir ${msg}`); }), parentPort.postMessage(trabajadorData); } si (esMainThread) { hilo principal(); } demás { hilo de trabajo(); }
const afirmar = require('assert'); constante { Obrero, canal de mensajes, puerto de mensajes, es hilo principal, puertopadre } = requerir('worker_threads'); si (esMainThread) { trabajador constante = nuevo trabajador (__nombre de archivo); const subChannel = nuevo MessageChannel(); trabajador.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('mensaje', (valor) => { console.log('recibido:', valor); }); } demás { parentPort.once('mensaje', (valor) => { afirmar (valor. aquí está la instancia de YourPort de MessagePort); value.hereIsYourPort.postMessage('el trabajador está enviando esto'); valor.aquíEsTuPuerto.close(); }); }
El proceso es la unidad más pequeña de asignación de recursos y el subproceso es la unidad más pequeña de programación de CPU.
IPC (comunicación entre procesos) es进程间通信
. Dado que cada proceso tiene su propio espacio de direcciones independiente después de la creación, el propósito de implementar IPC es compartir el acceso a los recursos entre procesos.
Hay muchas formas de implementar IPC: canalizaciones, colas de mensajes, semáforos, sockets de dominio y Node.js se implementa a través de canalizaciones.
De hecho, el proceso principal primero creará un canal IPC y escuchará este IPC antes de crear el proceso hijo, y luego creará el proceso hijo. Le indicará al proceso hijo y el descriptor de archivo relacionado con el canal IPC a través de la variable de entorno (. NODE_CHANNEL_FD). Se inicia el proceso hijo. En este momento, el canal IPC se conecta de acuerdo con el descriptor de archivo para establecer una conexión con el proceso padre.
Un identificador es una referencia que se puede utilizar para identificar un recurso. Contiene el descriptor del recurso del archivo que apunta al objeto.
Generalmente, cuando queremos monitorear múltiples procesos en un puerto, podemos considerar usar el agente de proceso principal:
Sin embargo, esta solución de proxy hará que cada recepción de solicitud y reenvío de proxy utilice dos descriptores de archivos, y los descriptores de archivos del sistema son limitados. Este enfoque afectará la escalabilidad del sistema.
Entonces, ¿por qué utilizar asas? La razón es que en escenarios de aplicaciones reales, establecer la comunicación IPC puede implicar escenarios de procesamiento de datos más complejos. El identificador se puede pasar como el segundo parámetro opcional del método send()
, lo que significa que el identificador de recurso se puede pasar directamente. La transmisión evita el uso de descriptores de archivos causados por el reenvío de proxy mencionado anteriormente.
Los siguientes son los tipos de identificadores que admiten el envío:
Después de que el proceso principal del proceso huérfano crea un proceso secundario, el proceso principal sale, pero uno o más procesos secundarios Los procesos correspondientes al proceso principal aún están vivos. Esto se ilustra mediante el siguiente ejemplo de código.
# trabajador.js const http = require ('http'); const servidor = http.createServer ((req, res) => { res.end ('Soy trabajador, pid:' + process.pid + ', ppid:' + process.ppid); // Registre el proceso actual del proceso del trabajador PID y el proceso principal PPID }); dejar trabajador; Process.on ('Mensaje', function (Mensaje, SendHandle) { if (mensaje === 'servidor') { trabajador = sendHandle; trabajador.on ('conexión', function (socket) { servidor.emit ('conexión', socket); }); }}
)
;
const bift = requirir ('child_process'). bifor; const servidor = require ('net'). CreateServer (); servidor.listen (3000); Const Worker = Fork ('Worker.js'); trabajador.send ('servidor', servidor); console.log ('Proceso de trabajo creado, PID: %S PPID: %S', Worker.pid, Process.pid); proceso.exit (0); // Después de crear el proceso infantil, el proceso principal sale
.
Dado que el proceso principal sale en Master.js, el monitor de actividad solo muestra el proceso del trabajador.
Verifique nuevamente, abra la interfaz de llamada de la consola, puede ver que el PPID correspondiente al proceso de trabajo 5611 es 1 (para el proceso de inicio), y se ha convertido en un proceso huérfano en este momento.
El proceso de demonio se ejecuta en segundo plano y no se ve afectado por el terminal.
Los estudiantes que desarrollan Node.js node app.js
estar familiarizados con él.前台运行模式
.
Si se utiliza el método de proceso de demonio, después de ejecutar node app.js
para iniciar un proceso de servicio en este terminal, también puedo hacer otras cosas en este terminal sin afectarnos.
Crear un proceso infantil
Cree una nueva sesión en el proceso infantil (llame a la función del sistema setsid)
Cambie el directorio de trabajo del proceso infantil (como: "/" o "/usr/, etc.)
termine el proceso principal
options.detached
. const spawn = require ('child_process'). Spawn; función startDaemon () { const demora = spawn ('nodo', ['daemon.js'], { CWD: '/usr', separado: verdadero, stdio: 'ignorar', }); console.log ('Proceso de demonio inicia el proceso principal PID: %s, Daemon Process PID: %S', Process.pid, Daemon.pid); Daemon.unref (); } startDaemon ()
La lógica de procesamiento en el archivo Daemon.js inicia un temporizador y lo ejecuta cada 10 segundos para que este recurso no salga al mismo tiempo, el registro está escrito a
/usr/daemon.jsen el directorio de trabajo actual del proceso infantil.
const fs = requerir('fs'); const {console} = require ('console'); // Logger simple personalizado const logger = new Console (fs.CreateWriteStream ('./ stdout.log'), fs.CreateWriteStream ('./ stderr.log')); setInterval (function () { logger.log ('Daemon Pid:', Process.pid, ', PPID:', Process.ppid);},
1000
*
10);
en el trabajo real, no somos ajenos a los procesos de demonio, como PM2, clúster de huevos, etc. Lo anterior es solo una simple demostración para explicar el proceso de demonio. El proceso de demonio sigue siendo muy alto.
5.¿Cuál es el
directoriode process.cwd()
Desde el directorio del proceso principal, que se puede obtener a través process.chdir()
¿Qué hace?
Por ejemplo, al leer un archivo a través de FS, si se establece en una ruta relativa, se buscará en relación con el directorio donde se inicia el proceso actual. El resultado correcto no se obtendrá. En otro caso, el módulo de terceros mencionado en el programa también se busca en función del directorio donde se inicia el proceso actual.
// Ejemplo de process.chdir ('/users/May/Documents/Test/') // Establecer el directorio de proceso actual console.log (process.cwd ());