Amigos que estão familiarizados com js sabem que js 是单线程
. No Node, um modelo multiprocesso de thread único é adotado. Devido à limitação de thread único do JavaScript, em servidores multi-core, muitas vezes precisamos iniciar vários processos para maximizar o desempenho do servidor.
Clusters de processos Node.js podem ser usados para executar várias instâncias do Node.js, que podem distribuir a carga de trabalho entre seus threads de aplicativos. Quando o isolamento do processo não for necessário, use o módulo worker_threads
, que permite que vários threads de aplicativo sejam executados em uma única instância do Node.js.
Node introduziu o módulo cluster após a versão V0.8,一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
O módulo cluster facilita a criação de processos filhos que compartilham portas de servidor.
A camada inferior do cluster é o módulo child_process. Além de enviar mensagens comuns, ele também pode enviar objetos subjacentes
TCP
,UDP
, etc.cluster
é uma aplicação combinada do módulochild_process
e do módulonet
. Quando o cluster for iniciado, o servidor TCP será iniciado internamente e o descritor de arquivo do soquete do servidor TCP será enviado ao processo de trabalho.
Na aplicação do módulo cluster
,一个主进程只能管理一组工作进程
. Seu modo operacional não é tão flexível quanto o módulo child_process
, mas é mais estável:
const cluster = require('cluster') e
.isMaster
identifica o processo principal, Node<16.isPrimary.isPrimary
o processo principal, Node>16.isWorker.isWorker
o subprocesso. .worker é.worker
pelo trabalho atual Referência ao objeto de processo [no processo filho].workers
armazena o hash do objeto de processo de trabalho ativo, com o campo id
como chave. Isso facilita o loop por todos os processos de trabalho. Está disponível apenas no processo principal. cluster.wokers[id] === worker
[no processo principal].settings
é somente leitura, item de configuração de cluster. Após chamar o método .setupPrimary() ou .fork(), este objeto de configurações conterá as configurações, incluindo valores padrão. Anteriormente um objeto vazio. Este objeto não deve ser alterado ou definido manualmente.cluster.settings
:- `execArgv` <string[]>Uma lista de parâmetros de string passados para o arquivo executável Node.js. **Padrão:** `process.execArgv`. - `exec` <string> Caminho do arquivo para o arquivo do processo de trabalho. **Padrão:** `process.argv[1]`. - `args` <string[]> Argumentos de string passados para o processo de trabalho. **Padrão:** `process.argv.slice(2)`. - `cwd` <string>O diretório de trabalho atual do processo de trabalho. **Padrão:** `indefinido` (herdado do processo pai). - `serialization` <string>Especifica o tipo de serialização usado para enviar mensagens entre processos. Os valores possíveis são `'json'` e `'avançado'`. **Padrão:** `falso`. - `silent` <boolean>Se enviar a saída para a entrada e saída padrão do processo pai. **Padrão:** `falso`. - `stdio` <Array> configura a entrada e saída padrão do processo gerado. Como o módulo de cluster depende do IPC para ser executado, esta configuração deve conter uma entrada `'ipc'`. Quando esta opção é fornecida, ela substitui `silent`. - `uid` <número> define o ID do usuário do processo. - `gid` <número> define o ID do grupo do processo. - `inspectPort` <número> | <Função> Define a porta do inspetor para o processo de trabalho. Pode ser um número ou uma função que não aceita parâmetros e retorna um número. Por padrão, cada processo de trabalho tem sua própria porta, começando no `process.debugPort` do processo principal e aumentando. - `windowsHide` <boolean> Oculta a janela do console do processo gerado normalmente criada em sistemas Windows. **Padrão:** `falso`.
.fork([env])
gera um novo processo de trabalho [no processo principal].setupPrimary([settings])
Node>16.setupMaster([settings])
é usado para alterar o comportamento padrão de 'fork', após o uso As configurações aparecerão em cluster.settings
. Quaisquer alterações nas configurações afetarão apenas chamadas futuras para .fork()
, que ainda não estejam em execução em processos de trabalho. Os valores padrão acima se aplicam apenas à primeira chamada. O nó é menor que 16 [No processo principal].disconnect([callback])
Chamado quando todos os processos de trabalho desconectam e fecham identificadores [No processo principal]Para tornar o cluster mais estável e robusto, cluster
também expõe muitos eventos Event:
'message'
, acionados quando o processo mestre do cluster recebe uma mensagem de qualquer processo de trabalho.'exit'
, quando qualquer processo de trabalho morre, o módulo do cluster acionará o evento 'exit'
.cluster.on('exit', (trabalhador, código, sinal) => { console.log('trabalhador %d morreu (%s). reiniciando...', trabalhador.process.pid, sinal || código); cluster.fork(); });
'listening'
, após chamar listen()
do processo de trabalho, quando o evento 'listening'
for acionado no servidor, cluster
no processo principal também acionará o evento 'listening'
.cluster.on('escutando', (trabalhador, endereço) => { console.log( `Um trabalhador agora está conectado a ${address.address}:${address.port}`); });
'fork'
, quando um novo processo de trabalho é gerado, o módulo de cluster acionará o evento 'fork'
.cluster.on('fork', (trabalhador) => { tempos limite[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
, acionado toda vez que .setupPrimary()
é chamado.disconnect
é acionado depois que o canal IPC do processo de trabalho é desconectado. Quando o processo de trabalho sai normalmente, é eliminado ou desconecta manualmentecluster.on('disconnect', (worker) => { console.log(`O trabalhador #${worker.id} foi desconectado`); });
O objeto Worker
contém todas as informações e métodos públicos do processo de trabalho. No processo principal, você pode usar cluster.workers
para obtê-lo. Em um processo de trabalho, você pode usar cluster.worker
para obtê-lo.
.id
Cada novo processo de trabalho recebe seu próprio id exclusivo. Este id é armazenado em id
. Quando um processo de trabalho está ativo, esta é a chave que o indexa em cluster.workers
..process
Todos os processos de trabalho são criados usando child_process.fork()
, e o objeto retornado por esta função é armazenado como .process
. No processo de trabalho, o process
global é armazenado..send(message[, sendHandle[, options]][, callback])
envia uma mensagem para o processo de trabalho ou para o processo principal e você pode optar por usar um identificador. No processo principal, envia uma mensagem para um processo de trabalho específico. É o mesmo que ChildProcess.send()
. No processo de trabalho, envia uma mensagem ao processo principal. É o mesmo que process.send()
..destroy()
.kill([signal])
Esta função encerrará o processo de trabalho. A função kill()
mata o processo de trabalho sem esperar por uma desconexão normal, ela tem o mesmo comportamento que worker.process.kill()
. Para compatibilidade com versões anteriores, este método tem o alias de worker.destroy()
..disconnect([callback])
é enviado ao processo de trabalho, fazendo com que ele chame seu próprio .disconnect()
que desligará todos os servidores, aguardará eventos 'close'
nesses servidores e, em seguida, desconectará o canal IPC..isConnect()
Esta função retorna true
se o processo de trabalho estiver conectado ao seu processo principal através de seu canal IPC, caso contrário, false
. Os processos de trabalho se conectam ao seu processo mestre após a criação..isDead()
Esta função retorna true
se o processo de trabalho foi encerrado (devido à saída ou recebimento de um sinal). Caso contrário, ele retorna false
.Para tornar o cluster mais estável e robusto, o módulo cluster
também expõe muitos eventos:
'message'
, no processo de trabalho.evento cluster.workers[id].on('message', messageHandler);
'exit'
, quando qualquer processo de trabalho morre,当前worker工作进程
acionará o evento 'exit'
.if (cluster.isPrimary) { const trabalhador = cluster.fork(); trabalhador.on('exit', (código, sinal) => { se (sinal) { console.log(`trabalhador foi morto por sinal: ${signal}`); } senão if (código! == 0) { console.log(`worker saiu com código de erro: ${code}`); } outro { console.log('sucesso do trabalhador!'); } }); }
'listening'
, chame listen()
do processo de trabalho para ouvir o processo de trabalho atual.cluster.fork().on('ouvindo', (endereço) => { // O processo de trabalho está escutando });
disconnect
, que é acionado após o canal IPC do processo de trabalho ser desconectado. Quando o processo de trabalho sai normalmente, é eliminado ou desconecta manualmentecluster.fork().on('disconnect', () => { //Limitado ao acionamento no objeto de trabalho atual});
No Node, a comunicação entre processos (IPC) é usada para realizar a comunicação entre processos entre o processo principal e os subprocessos. Método .send()
(a.send significa enviar uma mensagem para um Send) para enviar mensagens e ouvir eventos message
para coletar informações. Isso é implementado pelo cluster模块
integrando EventEmitter
. Também é um exemplo simples de comunicação entre processos no site oficial
process.on('message')
, process.send()
child.on('message')
, child.send()
#cluster.isMaster #cluster.fork() #cluster.workers # cluster.workers[id].on('message', messageHandler); # cluster.workers[id].send(); # process.on('mensagem', mensagemHandler); # process.send(); const cluster = require('cluster'); const http = requer('http'); # Processo principal if (cluster.isMaster) { // Acompanhe as solicitações http console.log(`Primário ${process.pid} está em execução`); deixe numReqs = 0; //Conta solicitações função manipulador de mensagem(msg) { if (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1; } } // Inicia trabalhadores e escuta mensagens contendo notifyRequest // Inicia o multiprocesso (número de núcleos da CPU) // Gera o processo de trabalho. const numCPUs = require('os').cpus().length; for (seja i = 0; i < numCPUs; i++) { console.log(i) cluster.fork(); } // o processo principal do trabalhador do cluster se comunica com os processos filhos for (const id in cluster.workers) { // ***Ouvir eventos de processos filhos cluster.workers[id].on('message', messageHandler); // ***Envia cluster.workers[id].send({ para o processo filho tipo: 'masterToWorker', de: 'mestre', dados: { número: Math.floor(Math.random() * 50) } }); } cluster.on('exit', (trabalhador, código, sinal) => { console.log(`trabalhador ${worker.process.pid} morreu`); }); } outro { # Processos filhos // Os processos de trabalho podem compartilhar qualquer conexão TCP // Neste exemplo, é um servidor HTTP // Os processos de trabalho possuem um servidor http. http.Server((req, res) => { res.writeHead(200); res.end('olá mundon'); //******! ! ! ! Notifique o mestre sobre a solicitação! ! ! ! ! ! ******* //****** Enviar process.send({ cmd: 'notifyRequest' }); //****** Ouça process.on('message', function(message) { //xxxxxx }) }).ouvir(8000); console.log(`Trabalhador ${process.pid} iniciado`); }
A comunicação entre processos NodeJS envolve apenas a passagem de mensagens e, na verdade, não transfere objetos.
Antes de enviar a mensagem, o método send()
montará a mensagem em um identificador e esta mensagem será serializada por JSON.stringify
. transmitidos através do canal IPC. Eles são todos strings e são restaurados em objetos por meio de JSON.parse
após a transmissão.
Há app.listen(port)
no código Ao bifurcar, por que vários processos podem escutar a mesma porta?
A razão é que o processo principal envia o identificador de um objeto de serviço pertencente ao processo principal para vários subprocessos através do método send(), portanto, para cada subprocesso, após restaurar o identificador, eles obtêm o mesmo objeto de serviço. Quando a rede Quando uma solicitação é feita ao servidor, o serviço do processo é preemptivo, portanto, nenhuma exceção será causada ao escutar na mesma porta.
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); for (seja i=0; i<cpus.length; i++) { const trabalhador = fork('trabalhador.js'); console.log('processo de trabalho criado, pid: %s ppid: %s', trabalhador.pid, process.pid); }
# trabalhador.js const http = requer('http'); http.createServer((req, res) => { res.end('Sou trabalhador, pid: ' + process.pid + ', ppid: ' + process.ppid); }).listen(3000);
No exemplo de código acima, quando o console executa
node master.js
apenas um trabalhador pode escutar a porta 3000 e o restante lançaráError: listen EADDRINUSE :::3000
erro.
发送句柄
entre processos após a versão v0.5.9/** * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * mensagem * enviarHandle */ subprocess.send(message, sendHandle)
Depois que o canal IPC é estabelecido entre os processos pai e filho, a mensagem é enviada através do método send do objeto subprocesso.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
, para resolver o problema de ocupação de porta multiprocesso acima, passamos o soquete do processo principal para o processo filho.
#master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); servidor const = require('net').createServer(); servidor.ouvir(3000); process.title = 'nó-mestre' for (seja i=0; i<cpus.length; i++) { const trabalhador = fork('trabalhador.js'); # Passe o identificador trabalhador.send('server', server); console.log('processo de trabalho criado, pid: %s ppid: %s', trabalhador.pid, process.pid); }
//trabalhador.js deixe trabalhador; process.title = 'node-worker' process.on('mensagem', function (mensagem, sendHandle) { if (mensagem === 'servidor') { trabalhador = enviarHandle; trabalhador.on('conexão', função (soquete) { console.log('Eu sou trabalhador, pid: ' + process.pid + ', ppid: ' + process.ppid) }); } });
Verifique se o console executa node master.js
Se você entender cluster
, saberá que processos filhos são criados por meio de cluster.fork()
. No Linux, o sistema fornece nativamente o método fork
, então por que o Node escolhe implementar cluster模块
sozinho em vez de usar diretamente o método nativo do sistema? Os principais motivos são os dois pontos a seguir:
O processo de bifurcação monitora a mesma porta, o que causará erros de ocupação da porta.
Não há balanceamento de carga entre os processos de bifurcação, o que pode facilmente levar ao fenômeno do rebanho trovejante
cluster模块
o primeiro problema, determinamos se o processo atual é master进程
, se for, escuta na porta. Caso contrário, é representado como um worker进程
fork e não escuta na porta.
Em resposta à segunda pergunta, cluster模块
possui uma função de balanceamento de carga integrada. master进程
é responsável por escutar a porta para receber solicitações e, em seguida, atribuí-las aos worker进程
correspondentes por meio do algoritmo de agendamento (o padrão é. Round-Robin, o algoritmo de escalonamento pode ser modificado através da variável de ambiente NODE_CLUSTER_SCHED_POLICY
).
Quando o código lança uma exceção que não é capturada, o processo será encerrado. Neste momento, o Node.js fornece process.on('uncaughtException', handler)
para capturá-la, mas quando um. Quando o processo Worker encontra uma exceção não detectada, ele já está em um estado incerto. Neste momento, devemos deixar o processo sair normalmente:
+---------+ +---------+ Trabalhador | +---------+ +----+----+ |exceção não capturada | +----------------+ | | +---------+ | | +----+----+ | desconectar | bifurcar um novo trabalhador | +------------------------> + ----------------------- -> | | espere... | | saída | +-------------> | | morrer | | |
Quando um processo tem uma exceção que causa uma falha ou OOM e é eliminado pelo sistema, ao contrário de quando ocorre uma exceção não detectada, ainda temos a chance de permitir que o processo continue a ser executado. o processo atual sai diretamente e o Master imediatamente bifurca um Novo Trabalhador.
O módulo child_process fornece a capacidade de derivar processos filhos, que é simplesmente执行cmd命令的能力
. Por padrão, stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. Esses pipelines têm capacidade limitada (e específica da plataforma). Se o processo filho exceder esse limite ao gravar em stdout e nenhuma saída for capturada, o processo filho será bloqueado e aguardará que o buffer do pipe aceite mais dados. Este é o mesmo comportamento de um tubo no shell. Se a saída não for consumida, use a opção { stdio: 'ignore' }.
const cp = require('child_process');
O processo filho criado por meio da API não possui conexão necessária com o processo pai.
4 métodos assíncronos são usados para criar processos filhos: fork, exec, execFile, spawn
Node.
fork(modulePath, args)
: Usado quando você deseja executar um processo do Node como um processo independente, de modo que o processamento do cálculo e o descritor do arquivo sejam separados do processo principal do Node (copiando um processo filho)não-Node
spawn(command, args)
: Processe alguns problemas Use execFile(file, args[, callback]) quando houver muitas E/Ss de subprocessos ou quando o processo tiver uma grande quantidade de saídaexecFile(file, args[, callback])
Use-o quando precisar executar apenas um programa externo. a velocidade de execução é rápida e relativamente segura para processar a entrada do usuárioexec(command, options)
: Usado quando você deseja acessar diretamente o comando shell do thread. Preste atenção aostrês métodos de sincronização inseridos pelo usuário: execSync
, execFileSync
, spawnSync
Os outros três métodos são extensões de spawn()
.
. O processo pai, exceto o canal de comunicação IPC estabelecido entre os dois. Cada processo possui sua própria memória e sua própria instância V8
.
Por exemplo, crie dois arquivos, worker.js e master.js, em um diretório:
#child.js. const t = JSON.parse(process.argv[2]); console.error(`processo filho t=${JSON.stringify(t)}`); process.send({hello:`son pid=${process.pid} por favor, dê ao pai o processo pid=${process.ppid} hello`}); process.on('mensagem', (msg)=>{ console.error(`mensagem do processo filho=${JSON.stringify(msg)}`); });
#pai.js const {fork} = require('child_process'); for(seja i = 0; i < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,nome:1})]); p.on('mensagem', (mensagem) => { console.log(`messsgae da criança msg=${JSON.stringify(msg)}`, ); }); p.send({olá:`Saudações do pai ${process.pid} process id=${i}`}); }
Inicie parent.js por meio de node parent.js
e, em seguida, verifique o número de processos por meio de ps aux | grep worker.js
Podemos descobrir que, idealmente, o número de processos é igual ao número de núcleos de CPU e cada processo usa um. Núcleo da CPU.
Este é o modo Master-Worker clássico (modo master-slave)
Na verdade, bifurcar um processo é caro e o objetivo de copiar um processo é aproveitar ao máximo os recursos da CPU, portanto, o NodeJS usa uma abordagem orientada a eventos em um único thread para resolver o problema de alta simultaneidade.
Cenários Aplicáveis <br/>Geralmente usados para cenários demorados e são implementados usando nó, como download de arquivos;
Fork pode implementar download multithread: dividir o arquivo em vários blocos, então cada processo baixa uma parte e finalmente os reúne
const cp = require('child_process'); // O primeiro parâmetro é o nome ou caminho do arquivo executável a ser executado. aqui está o eco cp.execFile('echo', ['olá', 'mundo'], (err, stdout, stderr) => { if (erro) {console.erro(erro); console.log('stdout:', stdout); console.log('stderr: ', stderr); });
Cenários aplicáveis <br/> Mais adequados para tarefas com baixa sobrecarga e mais atenção aos resultados, como ls, etc.
é usado principalmente para executar um método shell, e spawn é; ainda chamado internamente, mas há um limite máximo de cache.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | sort | uniq`, (err, stdout, stderr) => { console.log(stdout); });
Cenários aplicáveis <br/>Mais adequados para tarefas com baixa sobrecarga e prestando mais atenção aos resultados, como ls, etc.
tarefa única
const cp = require('child_process'); const filho = cp.spawn('echo', ['olá', 'mundo']); filho.on('erro', console.erro); # A saída é um fluxo, saída para o processo principal stdout, o console child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
concatenação multitarefa
const cp = require('child_process'); const caminho = require('caminho'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); const sort = cp.spawn('sort'); const uniq = cp.spawn('uniq'); #A saída é um fluxo cat.stdout.pipe(sort.stdin); classificar.stdout.pipe(uniq.stdin);uniq.stdout.pipe(
process.stdout
);
spawn é streaming, portanto é adequado para tarefas demoradas, como executar npm install e imprimir o processo de instalação
é acionado após o término do processo e o fluxo de entrada e saída padrão (sdtio) do o processo filho foi 'close'
. Este evento é diferente de exit
porque vários processos podem compartilhar o mesmo fluxo stdio.
Parâmetros:
Pergunta: O código precisa existir?
(Parece que não pelos comentários no código) Por exemplo, se você usar kill
para encerrar o processo filho, qual é o código?
parâmetros de saída:
código, sinal, se o processo filho sai sozinho, então code
é o código de saída, caso contrário é nulo;
Se o processo filho for encerrado por meio de um sinal, então signal
será o sinal para encerrar o processo, caso contrário, será nulo.
Dos dois, um não deve ser nulo.
Coisas a serem observadas :
Quando o evento exit
é acionado, o fluxo stdio do processo filho ainda pode estar aberto. (Cenário?) Além disso, o nodejs escuta os sinais SIGINT e SIGTERM. Ou seja, quando o nodejs recebe esses dois sinais, ele não sairá imediatamente. Em vez disso, ele fará algum trabalho de limpeza primeiro e depois lançará novamente esses dois sinais. (Visualmente, js pode fazer trabalhos de limpeza neste momento, como fechar o banco de dados, etc.)
SIGINT
: interrupção, sinal de encerramento do programa, geralmente emitido quando o usuário pressiona CTRL+C, usado para notificar o processo em primeiro plano para encerrar o processo.
SIGTERM
: terminar, sinal de fim do programa, este sinal pode ser bloqueado e processado e geralmente é usado para exigir que o programa saia normalmente. O comando shell kill gera este sinal por padrão. Se o sinal não puder ser encerrado, tentaremos SIGKILL (terminação forçada).
Quando as seguintes coisas acontecerem, um erro será acionado. Quando o erro é acionado, a saída pode ou não ser acionada. (O coração está partido)
é acionada quando process.send()
é usado para enviar uma mensagem.
parâmetro :
message
, é um objeto json ou valor primitivo; sendHandle
, um objeto net.Socket ou um objeto net.Server (alunos familiarizados com cluster devem estar familiarizados com isso)
: Ao chamar .disconnected()
, defina-o. para falso. Representa se ele pode receber mensagens do processo filho ou enviar mensagens para o processo filho.
.disconnect() : fecha o canal IPC entre o processo pai e o processo filho. Quando este método é chamado, o evento disconnect
será acionado. Se o processo filho for uma instância de nó (criada por meio de child_process.fork()), então process.disconnect()
também poderá ser chamado ativamente dentro do processo filho para encerrar o canal IPC.
multiprocessos geralmente são usados para simular o multithreading.
são ocupados pelo processo do Node. O núcleo do
o mecanismo v8. Depois que o Node for iniciado, uma instância da v8 será criada.
.单线程
, mas o ambiente host do Javascript, seja Node ou o navegador, é multithread.
Por que o Javascript é de thread único?
Esse problema precisa começar com o navegador. Para operações DOM no ambiente do navegador, imagine se vários threads operarem no mesmo DOM, será caótico. Isso significa que a operação DOM só pode ser feita de uma única maneira. evite conflitos de renderização de DOM. No ambiente do navegador, o thread de renderização da UI e o mecanismo de execução JS são mutuamente exclusivos. Quando um é executado, o outro será suspenso.
process.env.UV_THREADPOOL_SIZE = 64
worker_threads
para fornecer recursos reais de multi-threading para o Node.const {. éMainThread, parentPort, trabalhadorData, ID do tópico, Canal de mensagem, Porta de mensagem, Trabalhador } = require('worker_threads'); função mainThread() { for (seja i = 0; i < 5; i++) { const trabalhador = novo Trabalhador (__filename, { trabalhadorData: i }); trabalhador.on('exit', code => { console.log(`main: trabalhador interrompido com código de saída ${code}`); }); trabalhador.on('mensagem', mensagem => { console.log(`principal: receber ${msg}`); trabalhador.postMessage(mensagem + 1); }); } } função trabalhadorThread() { console.log(`trabalhador: trabalhadorDate ${workerData}`); parentPort.on('mensagem', mensagem => { console.log(`trabalhador: receba ${msg}`); }), parentPort.postMessage(workerData); } if (isMainThread) { mainThread(); } outro { trabalhadorThread(); }
const assert = require('assert'); const { Trabalhador, Canal de mensagem, Porta de mensagem, éMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const trabalhador = novo Trabalhador (__nome do arquivo); const subChannel = new MessageChannel(); trabalhador.postMessage({hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('mensagem', (valor) => { console.log('recebido:', valor); }); } outro { parentPort.once('mensagem', (valor) => { assert(value.hereIsYourPort instância de MessagePort); value.hereIsYourPort.postMessage('o trabalhador está enviando isto'); valor.hereIsYourPort.close(); }); }
O processo é a menor unidade de alocação de recursos e o thread é a menor unidade de agendamento da CPU.
(comunicação entre processos) é进程间通信
. Como cada processo possui seu próprio espaço de endereço independente após a criação, o objetivo da implementação do IPC é compartilhar o acesso aos recursos entre os processos.
Existem muitas maneiras de implementar IPC: pipes, filas de mensagens, semáforos, soquetes de domínio e Node.js são implementados por meio de pipes.
Na verdade, o processo pai primeiro criará um canal IPC e ouvirá esse IPC antes de criar o processo filho e, em seguida, criará o processo filho. Ele informará o processo filho e o descritor de arquivo relacionado ao canal IPC por meio da variável de ambiente (). NODE_CHANNEL_FD). O processo filho é iniciado. Neste momento, o canal IPC é conectado de acordo com o descritor de arquivo para estabelecer uma conexão com o processo pai.
Um identificador é uma referência que pode ser usada para identificar um recurso. Ele contém o descritor de recurso do arquivo que aponta para o objeto.
Geralmente, quando queremos monitorar vários processos em uma porta, podemos considerar o uso do agente de processo principal:
No entanto, esta solução de proxy fará com que cada recepção de solicitação e encaminhamento de proxy usem dois descritores de arquivo, e os descritores de arquivo do sistema serão limitados. Essa abordagem afetará a escalabilidade do sistema.
Então, por que usar alças? A razão é que em cenários de aplicação reais, o estabelecimento de comunicação IPC pode envolver cenários de processamento de dados mais complexos. O identificador pode ser passado como o segundo parâmetro opcional do método send()
, o que significa que o identificador de recurso pode ser passado diretamente. a transmissão evita o uso de descritores de arquivo causados pelo encaminhamento de proxy mencionado acima.
A seguir estão os tipos de identificador que suportam o envio:
Depois que o processo pai do processo órfão cria um processo filho, o processo pai é encerrado, mas um ou mais filhos Os processos correspondentes ao processo pai ainda estão vivos. Isso é ilustrado pelo exemplo de código a seguir.
# trabalhador.js const http = requer ('http'); const server = http.createServer ((req, res) => { res. end ('Sou trabalhador, PID:' + process.pid + ', ppid:' + process.ppid); // Registre o processo atual do Processo de Trabalhador e PODRO PAI }); deixar trabalhador; process.on ('message', function (mensagem, sendHandle) { if (mensagem === 'servidor') { trabalhador = sendHandle; trabalhador.on ('conexão', função (soquete) { server.emit ('conexão', soquete); }); }}
)
;
const fork = requer ('child_process'). fork; const server = requer ('net'). createServer (); Server.Listen (3000); const worker = fork ('trabalhador.js'); trabalhador.send ('servidor', servidor); console.log ('Processo do trabalhador criado, PID: %s ppid: %s', trabalhador.pid, process.pid); process.Exit (0); // Depois de criar o processo filho, o processo principal sai
.
Como o processo pai se sai no Master.js, o monitor de atividades mostra apenas o processo do trabalhador.
Verifique novamente, abra a interface do console de chamada, você pode ver que o PPID correspondente ao processo do trabalhador 5611 é 1 (para o processo init) e tornou -se um processo órfão no momento.
O processo daemon é executado em segundo plano e não é afetado pelo terminal.
Os alunos que desenvolvem o Node.js podem estar familiarizados com ele node app.js
前台运行模式
.
Se o método do processo Daemon for usado, depois de executar node app.js
para iniciar um processo de serviço neste terminal, também posso fazer outras coisas neste terminal sem se afetar.
Crie um processo filho
Crie uma nova sessão no processo filho (ligue para o conjunto de funções do sistema)
Altere o diretório de trabalho do processo filho (como: "/" ou "/usr/etc.)
encerrar o processo pai
options.detached
Terceira
const spawn = requer ('Child_process'). Spawn; função startDaemon () { const Daemon = Spawn ('Nó', ['daemon.js'], { CWD: '/usr', destacado: verdadeiro, stdio: 'ignorar', }); console.log ('DAEMON PROCESS inicia o processo dos pais PID: %s, Daemon Process pid: %s', process.pid, daemon.pid); Daemon.unref (); } StartDaemon ()
A
lógica de processamento no arquivo Daemon.js inicia um timer e o executa a cada 10 segundos para que esse recurso não saia.
do processo infantil.const fs = requer('fs'); const {console} = requer ('console'); // Logger simples 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);
No trabalho real, não somos estranhos a processos de daemon, como PM2, agrupamento de ovos, etc. O acima é apenas uma demonstração simples para explicar o processo daemon. O processo Daemon ainda é muito alto.
5.Qual é o
diretório de trabalhoprocess.cwd()
A partir do diretório do processo pai, que pode ser obtido por meio process.chdir()
que
ele faz?
resultado correto não será obtido. Em outro caso, o módulo de terceiros mencionado no programa também é pesquisado com base no diretório em que o processo atual é iniciado.
// Exemplo de processo.chdir ('/users/may/documents/test/') // Defina o diretório de processo atual Console.log (process.cwd ());