O Node foi projetado para lidar com operações de E/S com eficiência, mas você deve saber que alguns tipos de programas não são adequados para esse modo. Por exemplo, se você planeja usar o Node para lidar com uma tarefa intensiva em CPU, poderá bloquear o loop do evento e, assim, reduzir a resposta do programa. Uma alternativa é atribuir tarefas intensivas em CPU a um processo separado para manipular, liberando assim o loop do evento. O Node permite gerar um processo e usar esse novo processo como criança de seu processo pai. No nó, o processo filho pode se comunicar em duas vias com o processo pai e, em certa medida, o processo pai também pode monitorar e gerenciar o processo filho.
Outro caso em que você precisa usar um processo filho é quando deseja simplesmente executar um comando externo e deixar o nó obter o valor de retorno do comando. Por exemplo, você pode executar um comando UNIX, script ou outros comandos que não podem ser executados diretamente no nó.
Este capítulo mostrará como executar comandos externos, criar e se comunicar com crianças e encerrar as crianças. O objetivo é dar uma idéia de como concluir uma série de tarefas fora do processo do nó. Executar comandos externos Quando você precisa executar um comando ou executável do shell externo, você pode usar o módulo Child_process para importá -lo assim: A cópia do código é a seguinte: var Child_process = requer ('Child_Process') Em seguida, você pode usar a função EXEC no módulo para executar comandos externos: A cópia do código é a seguinte: var Exec = Child_process.exec; exec (comando, retorno de chamada); O primeiro parâmetro do EXEC é a string de comando Shell que você está preparando para executar, e o segundo parâmetro é uma função de retorno de chamada. Essa função de retorno de chamada será chamada quando o EXEC terminar de executar comandos externos ou ocorrer um erro. A função de retorno de chamada tem três parâmetros: erro, stdout, stderr, veja o exemplo a seguir: A cópia do código é a seguinte: EXEC ('LS', function (err, stdout, stderr) { // Nota do tradutor: se você usar o Windows, poderá alterá -lo para o comando do Windows, como DIR, e não o repetirei mais tarde. }); Se ocorrer um erro, o primeiro parâmetro será uma instância da classe de erro. Se o primeiro parâmetro não contiver um erro, o segundo parâmetro stdout conterá a saída padrão do comando. O último parâmetro contém saída de erro relacionada ao comando. Listagem 8-1 mostra um exemplo mais complexo de execução de comandos externos Listagem 8-1: Executar comandos externos (código fonte: Capítulo8/01_external_command.js) A cópia do código é a seguinte: // importe a função EXEC do módulo Child_process var Exec = requer ('Child_Process'). Exec; // chamando o comando "Cat *.js | wc -l" Exec ('Cat *.JS | WC l', function (err, stdout, stderr) {// Linha 4 // O comando sai ou a chamada falha if (err) { // falhou ao iniciar um processo externo Console.log ('Child_Process Exit, o código de erro é:', err.code); retornar; } } Na quarta linha, passamos "CAT *.JS | WC -L" como o primeiro parâmetro a exec. Você também pode tentar qualquer outro comando, desde que o comando que você tenha usado no shell esteja ok. Em seguida, assuma uma função de retorno de chamada como o segundo parâmetro, que será chamado quando ocorrer um erro ou o processo filho terminar. Você também pode passar um terceiro parâmetro opcional antes da função de retorno de chamada, que contém algumas opções de configuração, como: A cópia do código é a seguinte: var Exec = requer ('Child_Process'). Exec; var options = { Tempo limite: 1000, Killsignal: 'Sigkill' }; EXEC ('CAT *.JS | WC L', OPÇÕES, FUNÇÃO (ERR, STDOUT, STDERR) { //… }); Os parâmetros que podem ser usados são: 1.CWD - O diretório atual, você pode especificar o diretório de trabalho atual. 2. Codificação-O formato de codificação do conteúdo de saída do processo filho, o valor padrão é "UTF8", ou seja, a codificação UTF-8. Se a saída do processo filho não for UTF8, você poderá usar este parâmetro para defini -lo. Os formatos de codificação suportados são: A cópia do código é a seguinte: ASCII UTF8 UCS2 base64 Se você quiser saber mais sobre esses formatos de codificação suportados pelo Node, consulte o Capítulo 4 "Usando buffer para processar, codificar e decodificar dados binários". 1.Timeout - O tempo limite da execução do comando em milissegundos, o padrão é 0, ou seja, não há limite e aguarde até que o processo filho termine. 2.MaxBuffer - Especifica o número máximo de bytes permitidos pelo fluxo STDOUT e Stderr Stream. Se o valor máximo for atingido, o processo infantil será morto. O valor padrão é 200*1024. 3. KillSignal - o sinal final enviado ao processo filho quando o tempo limite ou o cache de saída atinge o valor máximo. O valor padrão é "sigterm", que enviará um sinal de terminação ao processo filho. Isso geralmente é usado para encerrar o processo de maneira ordenada. Ao usar sinais SIGTerm, o processo também pode processar ou reescrever o comportamento padrão do processador de sinal depois de recebê -lo. Se o processo de destino precisar, você poderá passar outros sinais para ele ao mesmo tempo (como o SIGUSR1). Você também pode optar por enviar um sinal Sigkill, que será processado pelo sistema operacional e forçar o processo infantil a ser encerrado imediatamente, para que qualquer operação de limpeza do processo filho não seja executada. Se você deseja controlar ainda mais o final do processo, poderá usar o comando Child_Process.Spawn, que será introduzido posteriormente. 1.EVN - Especifica a variável de ambiente passada para o processo filho. O padrão é nulo, o que significa que o processo filho herdará as variáveis de ambiente de todos os processos pais antes de ser criado. Nota: Usando a opção Killsignal, você pode enviar sinais para o processo de destino como uma string. No nó, o sinal existe como uma string. Aqui está uma lista de sinais Unix e operações padrão correspondentes: Você pode fornecer um conjunto de variáveis extensíveis do ambiente pai para o processo filho. Se você modificar diretamente o objeto Process.env, alterará as variáveis de ambiente de todos os módulos no processo do nó, o que causará muitos problemas. A alternativa é criar um novo objeto e copiar todos os parâmetros em processo.env, consulte o Exemplo 8-2: Listagem 8-2: Use variáveis de ambiente parametrizado para executar comandos (código-fonte: capítulo8/02_env_vars_augment.js) A cópia do código é a seguinte: var env = process.env, Varname, EnvCopy = {}, EXEC = requer ('Child_Process'). Exec; // copy process.env to EnvCopy para (vaname em EV) { EnvCopy [Varname] = Env [Varname]; } // Defina algumas variáveis personalizadas EnvCopy ['Custom Env var1'] = 'algum valor'; EnvCopy ['Custom Env var2'] = 'algum outro valor'; // Executa comandos usando process.env e variáveis personalizadas EXEC ('LS la', {Env: EnvCopy}, function (err, stdout, stderr) { if (err) {tiro err; } console.log ('stdout:', stdout); console.log ('stderr:', stderr); } No exemplo acima, uma variável EnvCopy é criada para salvar variáveis de ambiente. Primeiro, ele copia as variáveis de ambiente do processo do nó do processo.env e depois adiciona ou substitui algumas variáveis de ambiente que precisam ser modificadas e, finalmente, passa o EnvCopy como um parâmetro variável de ambiente para a função EXEC e executa comandos externos. Lembre -se de que as variáveis de ambiente são passadas entre os processos através do sistema operacional e todos os tipos de valores variáveis de ambiente chegam ao processo filho como seqüências de caracteres. Por exemplo, se o processo pai tomar o número 123 como uma variável de ambiente, o processo filho receberá "123" como uma string. No exemplo a seguir, dois scripts de nós serão criados no mesmo diretório: parent.js e Child.js. O primeiro script ligará para o segundo. Vamos criar esses dois arquivos: Listagem 8-3: Processo dos pais Define variáveis de ambiente (Capítulo8/03_environment_number_parent.js) A cópia do código é a seguinte: var Exec = requer ('Child_Process'). Exec; EXEC ('Node Child.js', {Env: {número: 123}}, function (err, stdout, stderr) { if (err) {tiro err; } console.log ('stdout:/n', stdout); console.log ('stderr:/n', stderr); }); Salve este código em parent.js. A seguir, é apresentado o código-fonte do processo infantil e salvá-los no filho.js (consulte o Exemplo 8-4) Exemplo 8-4: Variáveis de ambiente de parseses de subprocesso (Capítulo8/04_environment_number_child.js) A cópia do código é a seguinte: var number = process.env.number; console.log (typeof (número)); // → "String" número = parseint (número, 10); console.log (typeof (número)); // → "Número" Depois de salvar este arquivo como Child.js, você pode executar o seguinte comando neste diretório: A cópia do código é a seguinte: $ node parent.js Você verá a seguinte saída: A cópia do código é a seguinte: sdtou: corda número Stderr: Como você pode ver, embora o processo pai passe uma variável de ambiente numérico, o processo filho o recebe como uma string (consulte a segunda linha de saída) e, na terceira linha, você analisa a string em um número. Gerar processo infantil Como você pode ver, você pode usar a função Child_process.exec () para iniciar um processo externo e chamar sua função de retorno de chamada no final do processo. Isso é muito simples de usar, mas existem algumas desvantagens: 1. Além de usar os parâmetros da linha de comando e as variáveis de ambiente, EXEC () não pode se comunicar com os processos filhos. 2. A saída do processo infantil é armazenada em cache, para que você não possa transmitir, pode ficar sem memória Felizmente, o módulo Child_process do Node permite que a granularidade mais fina controla o início, a parada e outras operações convencionais dos processos filhos. Você pode iniciar um novo processo infantil no aplicativo. O Node fornece um canal de comunicação bidirecional, permitindo que os processos pai e filho enviem e recebam dados de string um do outro. O processo pai também pode ter algumas operações de gerenciamento para o processo filho, enviar sinais para o processo filho e fechar com força o processo filho. Crie um processo infantil Você pode usar a função Child_process.spawn para criar um novo processo infantil, consulte o Exemplo 8-5: Exemplo 8-5: Gere processo infantil. (Capítulo8/05_spawning_child.js) A cópia do código é a seguinte: // importar a função de desova do módulo Child_process var spawn = requer ('Child_process'). Spawn; // Gere processos infantis usados para executar o comando "Tail -f /var/log/system.log" var criança = spawn ('cauda', ['-f', '/var/log/system.log']); O código acima gera um processo filho usado para executar comandos da cauda e toma "-f" e "/bar/log/system.log" como parâmetros. O comando Tail monitorará o arquivo /var/log/system.og (se presente) e, em seguida, produzirá todos os novos dados anexados para o fluxo de saída padrão STDOUT. A função Spawn retorna um objeto de processo infantil, que é um objeto de ponteiro, encapsulando a interface de acesso do processo real. Neste exemplo, atribuímos a este novo descritor a uma variável chamada criança. Ouça os dados de processos filhos Qualquer alça de processo filho que contenha o atributo STDOUT levará a saída padrão do processo filho como um objeto de fluxo. Você pode vincular o evento de dados neste objeto de fluxo, para que, sempre que um bloco de dados estiver disponível, a função de retorno de chamada correspondente será chamada, consulte o exemplo a seguir: A cópia do código é a seguinte: // Imprima a saída do processo filho para o console Child.stdout.on ('dados', função (dados) { console.log ('Saída da cauda:' + dados); }); Sempre que o processo filho gera dados para o stdout de saída padrão, o processo pai é notificado e imprime os dados no console. Além da saída padrão, o processo possui outro fluxo de saída padrão: o fluxo de erro padrão, que geralmente é usado para produzir informações de erro. Neste exemplo, se o arquivo /var/log/system.log não existir, o processo de cauda produzirá uma mensagem semelhante ao seguinte: "/var/log/system.log: nenhum arquivo ou diretório". Ao ouvir o fluxo STDERR, o processo pai será notificado quando esse erro ocorrer. O processo pai pode ouvir fluxos de erro padrão como este: A cópia do código é a seguinte: Child.stderr.on ('dados', função (dados) { console.log ('Erro da cauda Saída:', dados); }); A propriedade Stderr, como o Stdout, também é um fluxo somente leitura. Sempre que um processo filho gera dados no fluxo de erro padrão, o processo pai será notificado e os dados de saída. Envie dados para o processo infantil Além de receber dados do fluxo de saída do processo filho, o processo pai também pode gravar dados na entrada padrão do processo filho através da propriedade Childpoces.stdin para enviar dados para o processo filho. O processo infantil pode ouvir os dados de entrada padrão através do processo. Exemplo 8-6 criará um programa contendo as seguintes funções: 1.+1 Aplicação: Um aplicativo simples que pode receber números inteiros da entrada padrão e adicione -os e, em seguida, produza o resultado após a adição ao fluxo de saída padrão. Como um serviço de computação simples, este aplicativo simula o processo do nó como um serviço externo que pode executar tarefas específicas. 2. Teste o cliente do aplicativo +1, envie um número inteiro aleatório e, em seguida, produza o resultado. Usado para demonstrar como o processo do nó gera um processo filho e, em seguida, permite que ele execute tarefas específicas. Use o código a seguir no Exemplo 8-6 para criar um arquivo chamado Plus_One.js: Exemplo 8-6: +1 Aplicativo (Capítulo8/06_plus_one.js) A cópia do código é a seguinte: // restaurar o fluxo de entrada padrão que é pausado por padrão process.stdin.resume (); process.stdin.on ('dados', função (dados) { número var; tentar { // analisa os dados de entrada em um número inteiro número = parseint (data.toString (), 10); // +1 número += 1; // resultado de saída process.stdout.write (número + "/n"); } catch (err) { process.stderr.write (err.message + "/n"); } }); No código acima, aguardamos os dados do fluxo de entrada padrão do Stdin. Sempre que os dados estão disponíveis, assumimos que é um número inteiro e analisá -los em uma variável inteira, adicione 1 e emitir o resultado ao fluxo de saída padrão. Você pode executar este programa através do seguinte comando: A cópia do código é a seguinte: $ node plus_one.js Após a execução, o programa começa a aguardar a entrada. Se você entrar em um número inteiro e pressionar Enter, verá um número depois de ser adicionado 1 a ser exibido na tela. Você pode sair do programa pressionando Ctrl-C. Um cliente de teste Agora você deseja criar um processo de nó para usar os serviços de computação fornecidos pelo "aplicativo +1" anterior. Primeiro, crie um arquivo chamado Plus_One_Test.js, consulte o Exemplo 8-7: Exemplo 8-7: Teste +1 Aplicativo (Capítulo8/07_plus_one_test.js) A cópia do código é a seguinte: var spawn = requer ('Child_process'). Spawn; // Gere um processo filho para executar o aplicativo +1 var filho = spawn ('nó', ['plus_one.js']); // Ligue para a função a cada segundo setInterval (function () { // Crie um número aleatório menor que 10.000 var número = math.floor (math.random () * 10000); // Envie esse número para o processo filho: Child.stdin.write (número + "/n"); // Obtenha a resposta do processo filho e imprimi -lo: Child.stdout.once ('dados', função (dados) { console.log ('Child respondeu a' + número + 'com:' + dados); }); }, 1000); Child.stderr.on ('dados', função (dados) { process.stdout.write (dados); }); Da primeira linha à quarta linha, um processo infantil é iniciado para executar o "aplicativo +1" e, em seguida, as seguintes operações são executadas a cada segundo usando a função setInterval: 1. Crie um novo número aleatório inferior a 10000 2. Passe esse número como uma string para o processo infantil 3. Aguarde o processo da criança responder a uma string 4. Como você deseja receber apenas 1 número por vez, você precisa usar child.stdout.once em vez de child.stdout.on. Se o último for usado, uma função de retorno de chamada de um evento de dados será registrada a cada 1 segundo. Cada função de retorno de chamada registrada será executada quando o stdout do processo filho receber dados. Dessa forma, você descobrirá que o mesmo resultado de cálculo será emitido várias vezes. Esse comportamento está obviamente errado. Receber notificações quando o processo infantil sair Quando o processo infantil sair, o evento de saída será demitido. Exemplo 8-8 mostra como ouvi-lo: Exemplo 8-8: Ouça o evento de saída do processo infantil (capítulo8/09_listen_child_exit.js) A cópia do código é a seguinte: var spawn = requer ('Child_process'). Spawn; // gerar processo infantil para executar o comando "ls -la" var criança = spawn ('ls', ['-la']); Child.stdout.on ('dados', função (dados) { console.log ('Dados da criança:' + dados); }); // Quando o processo infantil sai: <strong> Child.on ('Exit', function (código) { console.log ('Processo infantil terminado com código' + código); }); </strong> Nas últimas linhas de código preto, o processo pai usa o evento de saída do processo infantil para ouvir seu evento de saída. Quando o evento ocorre, o console exibe a saída correspondente. O código de saída do processo filho será passado para a função de retorno de chamada como o primeiro parâmetro. Alguns programas usam um código de saída não 0 para representar um determinado estado de falha. Por exemplo, se você tentar executar o comando "ls al click filename.txt", mas o diretório atual não possui esse arquivo, você receberá um código de saída com um valor de 1, consulte o Exemplo 8-9: Exemplo 8-9: Obtenha o código de saída do processo infantil (capítulo8/10_child_exit_code.js) A cópia do código é a seguinte: var spawn = requer ('Child_process'). Spawn; // Gere processo infantil e execute o comando "ls dona_not_exist.txt" var criança = spawn ('ls', ['dona_not_exist.txt']); // Quando o processo infantil sai Child.on ('Exit', function (Code) { console.log ('Processo infantil terminado com código' + código); }); Neste exemplo, o evento de saída aciona a função de retorno de chamada e passa o código de saída do processo filho como o primeiro parâmetro. Se o processo infantil sair de forma anormal devido a ser morta por um sinal, o código de sinal correspondente será passado para a função de retorno de chamada como um segundo parâmetro, como no Exemplo 8-10: Listagem 8-10: Obtenha o sinal de saída do processo infantil (capítulo8/11_child_exit_signal.js) A cópia do código é a seguinte: var spawn = requer ('Child_process'). Spawn; // Gere processo infantil e execute o comando "Sleep 10" var criança = spawn ('sono', ['10']); setTimeout (function () { Child.kill (); }, 1000); Child.on ('Exit', function (código, sinal) { if (code) { console.log ('Processo infantil terminado com código' + código); } else if (signal) { console.log ('Processo infantil terminado devido ao sinal' + sinal); } }); Neste exemplo, um processo infantil é iniciado para realizar o sono 10 segundos, mas um sinal de sigkill é enviado ao processo filho antes de 10 segundos, o que resultará na seguinte saída: A cópia do código é a seguinte: O processo infantil terminou devido ao sinal sigterm Envie um sinal e mate o processo Nesta seção, você aprenderá a usar sinais para gerenciar subprocessos. Os sinais são uma maneira fácil de um processo pai se comunicar com crianças e até matar crianças. Códigos de sinal diferentes representam significados diferentes, e existem muitos sinais, alguns dos quais são os mais comuns usados para matar processos. Se um processo receber um sinal de que não sabe como lidar, o programa será interrompido pela exceção. Alguns sinais são processados por subprocessos, enquanto outros só podem ser processados pelo sistema operacional. Geralmente, você pode usar o método Child.kill para enviar um sinal ao processo filho e enviar um sinal de sigmterm por padrão: A cópia do código é a seguinte: var spawn = requer ('Child_process'). Spawn; var criança = spawn ('sono', ['10']); setTimeout (function () { Child.kill (); }, 1000); Você também pode enviar um sinal específico passando em uma string que identifica o sinal como o único parâmetro do método de matar: A cópia do código é a seguinte: Child.Kill ('SIGUSR2'); Deve -se notar que, embora o nome desse método seja matado, o sinal enviado não mata necessariamente o processo da criança. Se a criança processou o sinal, o comportamento do sinal padrão foi substituído. Os subprocessos escritos no nó podem reescrever a definição do processador de sinal como o seguinte: A cópia do código é a seguinte: Process.on ('SIGUSR2', function () { console.log ('obteve um sinal SIGUSR2'); }); Agora, você definiu o processador de sinal SIGUSR2. Quando o seu processo recebe o sinal SIGUSR2 novamente, ele não será morto, mas produz a frase "recebeu um sinal SIGUSR2". Usando esse mecanismo, você pode projetar uma maneira simples de se comunicar com o processo filho ou até comandá -lo. Embora não seja tão rico quanto o uso de entrada padrão, esse método é muito mais simples. resumo Neste capítulo, aprendemos a usar o método Child_Process.Exec para executar comandos externos. Dessa forma, podemos passar os parâmetros para o processo filho em vez de usar parâmetros da linha de comando, mas, em vez disso, definir variáveis de ambiente. Também aprendi a gerar processos filhos, chamando o método Child_Process.spawn para chamar comandos externos. Dessa forma, você pode usar fluxos de entrada e fluxos de saída para se comunicar com processos filhos ou usar sinais para se comunicar com processos infantis e matar.