欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Node.js中的child_process模塊詳解

 更新時(shí)間:2018年06月08日 11:37:40   作者:Randal  
這篇文章主要給大家介紹了關(guān)于Node.js中child_process模塊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

本文主要給大家介紹了關(guān)于Node.js中child_process模塊的相關(guān)內(nèi)容,在介紹child_process模塊之前,先來(lái)看一個(gè)例子。

const http = require('http');
const longComputation = () => {
 let sum = 0;
 for (let i = 0; i < 1e10; i++) {
 sum += i;
 };
 return sum;
};
const server = http.createServer();
server.on('request', (req, res) => {
 if (req.url === '/compute') {
 const sum = longComputation();
 return res.end(`Sum is ${sum}`);
 } else {
 res.end('Ok')
 }
});

server.listen(3000);

可以試一下使用上面的代碼啟動(dòng)Node.js服務(wù),然后打開(kāi)兩個(gè)瀏覽器選項(xiàng)卡分別訪問(wèn)/compute和/,可以發(fā)現(xiàn)node服務(wù)接收到/compute請(qǐng)求時(shí)會(huì)進(jìn)行大量的數(shù)值計(jì)算,導(dǎo)致無(wú)法響應(yīng)其他的請(qǐng)求(/)。

在Java語(yǔ)言中可以通過(guò)多線程的方式來(lái)解決上述的問(wèn)題,但是Node.js在代碼執(zhí)行的時(shí)候是單線程的,那么Node.js應(yīng)該如何解決上面的問(wèn)題呢?其實(shí)Node.js可以創(chuàng)建一個(gè)子進(jìn)程執(zhí)行密集的cpu計(jì)算任務(wù)(例如上面例子中的longComputation)來(lái)解決問(wèn)題,而child_process模塊正是用來(lái)創(chuàng)建子進(jìn)程的。

創(chuàng)建子進(jìn)程的方式

child_process提供了幾種創(chuàng)建子進(jìn)程的方式

  • 異步方式:spawn、exec、execFile、fork
  • 同步方式:spawnSync、execSync、execFileSync

首先介紹一下spawn方法

child_process.spawn(command[, args][, options])

command: 要執(zhí)行的指令
args: 傳遞參數(shù)
options: 配置項(xiàng)
const { spawn } = require('child_process');
const child = spawn('pwd');

pwd是shell的命令,用于獲取當(dāng)前的目錄,上面的代碼執(zhí)行完控制臺(tái)并沒(méi)有任何的信息輸出,這是為什么呢?

控制臺(tái)之所以不能看到輸出信息的原因是由于子進(jìn)程有自己的stdio流(stdin、stdout、stderr),控制臺(tái)的輸出是與當(dāng)前進(jìn)程的stdio綁定的,因此如果希望看到輸出信息,可以通過(guò)在子進(jìn)程的stdout 與當(dāng)前進(jìn)程的stdout之間建立管道實(shí)現(xiàn)

child.stdout.pipe(process.stdout);

也可以監(jiān)聽(tīng)事件的方式(子進(jìn)程的stdio流都是實(shí)現(xiàn)了EventEmitter API的,所以可以添加事件監(jiān)聽(tīng))

child.stdout.on('data', function(data) {
 process.stdout.write(data);
});

在Node.js代碼里使用的console.log其實(shí)底層依賴的就是process.stdout

除了建立管道之外,還可以通過(guò)子進(jìn)程和當(dāng)前進(jìn)程共用stdio的方式來(lái)實(shí)現(xiàn)

const { spawn } = require('child_process');
const child = spawn('pwd', {
 stdio: 'inherit'
});

stdio選項(xiàng)用于配置父進(jìn)程和子進(jìn)程之間建立的管道,由于stdio管道有三個(gè)(stdin, stdout, stderr)因此stdio的三個(gè)可能的值其實(shí)是數(shù)組的一種簡(jiǎn)寫

  • pipe 相當(dāng)于['pipe', 'pipe', 'pipe'](默認(rèn)值)
  • ignore 相當(dāng)于['ignore', 'ignore', 'ignore']
  • inherit 相當(dāng)于[process.stdin, process.stdout, process.stderr]

由于inherit方式使得子進(jìn)程直接使用父進(jìn)程的stdio,因此可以看到輸出

ignore用于忽略子進(jìn)程的輸出(將/dev/null指定為子進(jìn)程的文件描述符了),因此當(dāng)ignore時(shí)child.stdout是null。

spawn默認(rèn)情況下并不會(huì)創(chuàng)建子shell來(lái)執(zhí)行命令,因此下面的代碼會(huì)報(bào)錯(cuò)

const { spawn } = require('child_process');
const child = spawn('ls -l');
child.stdout.pipe(process.stdout);

// 報(bào)錯(cuò)
events.js:167
  throw er; // Unhandled 'error' event
  ^

Error: spawn ls -l ENOENT
 at Process.ChildProcess._handle.onexit (internal/child_process.js:229:19)
 at onErrorNT (internal/child_process.js:406:16)
 at process._tickCallback (internal/process/next_tick.js:63:19)
 at Function.Module.runMain (internal/modules/cjs/loader.js:746:11)
 at startup (internal/bootstrap/node.js:238:19)
 at bootstrapNodeJSCore (internal/bootstrap/node.js:572:3)
Emitted 'error' event at:
 at Process.ChildProcess._handle.onexit (internal/child_process.js:235:12)
 at onErrorNT (internal/child_process.js:406:16)
 [... lines matching original stack trace ...]
 at bootstrapNodeJSCore (internal/bootstrap/node.js:572:3)

如果需要傳遞參數(shù)的話,應(yīng)該采用數(shù)組的方式傳入

const { spawn } = require('child_process');
const child = spawn('ls', ['-l']);
child.stdout.pipe(process.stdout);

如果要執(zhí)行l(wèi)s -l | wc -l命令的話可以采用創(chuàng)建兩個(gè)spawn命令的方式

const { spawn } = require('child_process');
const child = spawn('ls', ['-l']);
const child2 = spawn('wc', ['-l']);
child.stdout.pipe(child2.stdin);
child2.stdout.pipe(process.stdout);

也可以使用exec

const { exec } = require('child_process');
exec('ls -l | wc -l', function(err, stdout, stderr) {
 console.log(stdout);
});

由于exec會(huì)創(chuàng)建子shell,所以可以直接執(zhí)行shell管道命令。spawn采用流的方式來(lái)輸出命令的執(zhí)行結(jié)果,而exec也是將命令的執(zhí)行結(jié)果緩存起來(lái)統(tǒng)一放在回調(diào)函數(shù)的參數(shù)里面,因此exec只適用于命令執(zhí)行結(jié)果數(shù)據(jù)小的情況。

其實(shí)spawn也可以通過(guò)配置shell option的方式來(lái)創(chuàng)建子shell進(jìn)而支持管道命令,如下所示

const { spawn, execFile } = require('child_process');
const child = spawn('ls -l | wc -l', {
 shell: true
});
child.stdout.pipe(process.stdout);

配置項(xiàng)除了stdio、shell之外還有cwd、env、detached等常用的選項(xiàng)

cwd用于修改命令的執(zhí)行目錄

const { spawn, execFile, fork } = require('child_process');
const child = spawn('ls -l | wc -l', {
 shell: true,
 cwd: '/usr'
});
child.stdout.pipe(process.stdout);

env用于指定子進(jìn)程的環(huán)境變量(如果不指定的話,默認(rèn)獲取當(dāng)前進(jìn)程的環(huán)境變量)

const { spawn, execFile, fork } = require('child_process');
const child = spawn('echo $NODE_ENV', {
 shell: true,
 cwd: '/usr'
});
child.stdout.pipe(process.stdout);
NODE_ENV=randal node b.js

// 輸出結(jié)果
randal

如果指定env的話就會(huì)覆蓋掉默認(rèn)的環(huán)境變量,如下

const { spawn, execFile, fork } = require('child_process');
spawn('echo $NODE_TEST $NODE_ENV', {
 shell: true,
 stdio: 'inherit',
 cwd: '/usr',
 env: {
 NODE_TEST: 'randal-env'
 }
});

NODE_ENV=randal node b.js

// 輸出結(jié)果
randal

detached用于將子進(jìn)程與父進(jìn)程斷開(kāi)連接

例如假設(shè)存在一個(gè)長(zhǎng)時(shí)間運(yùn)行的子進(jìn)程

// timer.js
while(true) {

}

但是主進(jìn)程并不需要長(zhǎng)時(shí)間運(yùn)行的話就可以用detached來(lái)斷開(kāi)二者之間的連接

const { spawn, execFile, fork } = require('child_process');
const child = spawn('node', ['timer.js'], {
 detached: true,
 stdio: 'ignore'
});
child.unref();

當(dāng)調(diào)用子進(jìn)程的unref方法時(shí),同時(shí)配置子進(jìn)程的stdio為ignore時(shí),父進(jìn)程就可以獨(dú)立退出了

execFile與exec不同,execFile通常用于執(zhí)行文件,而且并不會(huì)創(chuàng)建子shell環(huán)境

fork方法是spawn方法的一個(gè)特例,fork用于執(zhí)行js文件創(chuàng)建Node.js子進(jìn)程。而且fork方式創(chuàng)建的子進(jìn)程與父進(jìn)程之間建立了IPC通信管道,因此子進(jìn)程和父進(jìn)程之間可以通過(guò)send的方式發(fā)送消息。

注意:fork方式創(chuàng)建的子進(jìn)程與父進(jìn)程是完全獨(dú)立的,它擁有單獨(dú)的內(nèi)存,單獨(dú)的V8實(shí)例,因此并不推薦創(chuàng)建很多的Node.js子進(jìn)程

fork方式的父子進(jìn)程之間的通信參照下面的例子

parent.js

const { fork } = require('child_process');
const forked = fork('child.js');
forked.on('message', (msg) => {
 console.log('Message from child', msg);
});
forked.send({ hello: 'world' });

child.js

process.on('message', (msg) => {
 console.log('Message from parent:', msg);
});

let counter = 0;

setInterval(() => {
 process.send({ counter: counter++ });
}, 1000);
node parent.js

// 輸出結(jié)果
Message from parent: { hello: 'world' }
Message from child { counter: 0 }
Message from child { counter: 1 }
Message from child { counter: 2 }
Message from child { counter: 3 }
Message from child { counter: 4 }
Message from child { counter: 5 }
Message from child { counter: 6 }

回到本文初的那個(gè)問(wèn)題,我們就可以將密集計(jì)算的邏輯放到單獨(dú)的js文件中,然后再通過(guò)fork的方式來(lái)計(jì)算,等計(jì)算完成時(shí)再通知主進(jìn)程計(jì)算結(jié)果,這樣避免主進(jìn)程繁忙的情況了。

compute.js

const longComputation = () => {
 let sum = 0;
 for (let i = 0; i < 1e10; i++) {
 sum += i;
 };
 return sum;
};

process.on('message', (msg) => {
 const sum = longComputation();
 process.send(sum);
});

index.js

const http = require('http');
const { fork } = require('child_process');
const server = http.createServer();
server.on('request', (req, res) => {
 if (req.url === '/compute') {
 const compute = fork('compute.js');
 compute.send('start');
 compute.on('message', sum => {
  res.end(`Sum is ${sum}`);
 });
 } else {
 res.end('Ok')
 }
});
server.listen(3000);

監(jiān)聽(tīng)進(jìn)程事件

通過(guò)前述幾種方式創(chuàng)建的子進(jìn)程都實(shí)現(xiàn)了EventEmitter,因此可以針對(duì)進(jìn)程進(jìn)行事件監(jiān)聽(tīng)

常用的事件包括幾種:close、exit、error、message

close事件當(dāng)子進(jìn)程的stdio流關(guān)閉的時(shí)候才會(huì)觸發(fā),并不是子進(jìn)程exit的時(shí)候close事件就一定會(huì)觸發(fā),因?yàn)槎鄠€(gè)子進(jìn)程可以共用相同的stdio。

close與exit事件的回調(diào)函數(shù)有兩個(gè)參數(shù)code和signal,code代碼子進(jìn)程最終的退出碼,如果子進(jìn)程是由于接收到signal信號(hào)終止的話,signal會(huì)記錄子進(jìn)程接受的signal值。

先看一個(gè)正常退出的例子

const { spawn, exec, execFile, fork } = require('child_process');
const child = exec('ls -l', {
 timeout: 300
});
child.on('exit', function(code, signal) {
 console.log(code);
 console.log(signal);
});

// 輸出結(jié)果
0
null

再看一個(gè)因?yàn)榻邮盏絪ignal而終止的例子,應(yīng)用之前的timer文件,使用exec執(zhí)行的時(shí)候并指定timeout

const { spawn, exec, execFile, fork } = require('child_process');
const child = exec('node timer.js', {
 timeout: 300
});
child.on('exit', function(code, signal) {
 console.log(code);
 console.log(signal);
});
// 輸出結(jié)果
null
SIGTERM

注意:由于timeout超時(shí)的時(shí)候error事件并不會(huì)觸發(fā),并且當(dāng)error事件觸發(fā)時(shí)exit事件并不一定會(huì)被觸發(fā)

error事件的觸發(fā)條件有以下幾種:

  • 無(wú)法創(chuàng)建進(jìn)程
  • 無(wú)法結(jié)束進(jìn)程
  • 給進(jìn)程發(fā)送消息失敗

注意當(dāng)代碼執(zhí)行出錯(cuò)的時(shí)候,error事件并不會(huì)觸發(fā),exit事件會(huì)觸發(fā),code為非0的異常退出碼

const { spawn, exec, execFile, fork } = require('child_process');
const child = exec('ls -l /usrs');
child.on('error', function(code, signal) {
 console.log(code);
 console.log(signal);
});
child.on('exit', function(code, signal) {
 console.log('exit');
 console.log(code);
 console.log(signal);
});

// 輸出結(jié)果
exit
1
null

message事件適用于父子進(jìn)程之間建立IPC通信管道的時(shí)候的信息傳遞,傳遞的過(guò)程中會(huì)經(jīng)歷序列化與反序列化的步驟,因此最終接收到的并不一定與發(fā)送的數(shù)據(jù)相一致。

sub.js

process.send({ foo: 'bar', baz: NaN });
const cp = require('child_process');
const n = cp.fork(`${__dirname}/sub.js`);

n.on('message', (m) => {
 console.log('got message:', m); // got message: { foo: 'bar', baz: null }
});

關(guān)于message有一種特殊情況要注意,下面的message并不會(huì)被子進(jìn)程接收到

const { fork } = require('child_process');
const forked = fork('child.js');
forked.send({
 cmd: "NODE_foo",
 hello: 'world'
});

當(dāng)發(fā)送的消息里面包含cmd屬性,并且屬性的值是以NODE_開(kāi)頭的話,這樣的消息是提供給Node.js本身保留使用的,因此并不會(huì)發(fā)出message事件,而是會(huì)發(fā)出internalMessage事件,開(kāi)發(fā)者應(yīng)該避免這種類型的消息,并且應(yīng)當(dāng)避免監(jiān)聽(tīng)internalMessage事件。

message除了發(fā)送字符串、object之外還支持發(fā)送server對(duì)象和socket對(duì)象,正因?yàn)橹С謘ocket對(duì)象才可以做到多個(gè)Node.js進(jìn)程監(jiān)聽(tīng)相同的端口號(hào)。

未完待續(xù)......

參考資料

https://medium.freecodecamp.org/node-js-child-processes-everything-you-need-to-know-e69498fe970a
https://nodejs.org/dist/latest-v10.x/docs/api/child_process.html

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    這篇文章主要介紹了Node.js從字符串生成文件流的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • node.js?中的require用法和import的區(qū)別解析

    node.js?中的require用法和import的區(qū)別解析

    在Node.js中,require是一個(gè)內(nèi)置的函數(shù),用于在當(dāng)前模塊中加載和緩存其他模塊,這篇文章給大家介紹node.js?中的require用法和import的區(qū)別,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • 在Node.js中實(shí)現(xiàn)獲取用戶頻道信息的功能

    在Node.js中實(shí)現(xiàn)獲取用戶頻道信息的功能

    在構(gòu)建社交或視頻分享平臺(tái)時(shí),允許用戶查看其他用戶的頻道信息是一個(gè)基本需求,本文將介紹如何在Node.js應(yīng)用中,使用Express框架和Mongoose庫(kù)來(lái)實(shí)現(xiàn)這一功能,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-04-04
  • koa2 從入門到精通(小結(jié))

    koa2 從入門到精通(小結(jié))

    這篇文章主要介紹了koa2 從入門到精通,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • node-webkit打包成exe文件被360誤報(bào)木馬的解決方法

    node-webkit打包成exe文件被360誤報(bào)木馬的解決方法

    這篇文章主要介紹了node-webkit打包成exe文件被360誤報(bào)木馬的解決方法的相關(guān)資料,需要的朋友可以參考下
    2015-03-03
  • npm下載慢或下載失敗問(wèn)題解決的三種方法

    npm下載慢或下載失敗問(wèn)題解決的三種方法

    這篇文章主要為大家介紹了npm下載慢或下載失敗問(wèn)題解決的三種方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • nodeJS與MySQL實(shí)現(xiàn)分頁(yè)數(shù)據(jù)以及倒序數(shù)據(jù)

    nodeJS與MySQL實(shí)現(xiàn)分頁(yè)數(shù)據(jù)以及倒序數(shù)據(jù)

    這篇文章主要介紹了nodeJS與MySQL實(shí)現(xiàn)分頁(yè)數(shù)據(jù)以及倒序數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 一文詳解npm如何更新項(xiàng)目最新依賴包

    一文詳解npm如何更新項(xiàng)目最新依賴包

    npm包的更新速度很快,為了將項(xiàng)目或者全局依賴更新到最新版本,下面這篇文章主要給大家介紹了關(guān)于npm如何更新項(xiàng)目最新依賴包的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Node.js實(shí)現(xiàn)連接mysql數(shù)據(jù)庫(kù)功能示例

    Node.js實(shí)現(xiàn)連接mysql數(shù)據(jù)庫(kù)功能示例

    這篇文章主要介紹了Node.js實(shí)現(xiàn)連接mysql數(shù)據(jù)庫(kù)功能,簡(jiǎn)單分析了nodejs連接數(shù)據(jù)庫(kù)的操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • 前端自動(dòng)化開(kāi)發(fā)之Node.js的環(huán)境搭建教程

    前端自動(dòng)化開(kāi)發(fā)之Node.js的環(huán)境搭建教程

    這篇文章主要介紹了前端自動(dòng)化開(kāi)發(fā)之Node.js環(huán)境搭建的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用node.js具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-04-04

最新評(píng)論