成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Node.js child_process模塊解讀

baiy / 1029人閱讀

摘要:而且方式創(chuàng)建的子進(jìn)程與父進(jìn)程之間建立了通信管道,因此子進(jìn)程和父進(jìn)程之間可以通過的方式發(fā)送消息。與事件的回調(diào)函數(shù)有兩個參數(shù)和,代碼子進(jìn)程最終的退出碼,如果子進(jìn)程是由于接收到信號終止的話,會記錄子進(jìn)程接受的值。

在介紹child_process模塊之前,先來看一個下面的代碼。

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);

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

在Java語言中可以通過多線程的方式來解決上述的問題,但是Node.js在代碼執(zhí)行的時候是單線程的,那么Node.js應(yīng)該如何解決上面的問題呢?其實(shí)Node.js可以創(chuàng)建一個子進(jìn)程執(zhí)行密集的cpu計算任務(wù)(例如上面例子中的longComputation)來解決問題,而child_process模塊正是用來創(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: 配置項
const { spawn } = require("child_process");
const child = spawn("pwd");

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

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

child.stdout.pipe(process.stdout);

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

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

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

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

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

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

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時child.stdout是null。

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

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

// 報錯
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í)行ls -l | wc -l命令的話可以采用創(chuàng)建兩個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會創(chuàng)建子shell,所以可以直接執(zhí)行shell管道命令。spawn采用流的方式來輸出命令的執(zhí)行結(jié)果,而exec也是將命令的執(zhí)行結(jié)果緩存起來統(tǒng)一放在回調(diào)函數(shù)的參數(shù)里面,因此exec只適用于命令執(zhí)行結(jié)果數(shù)據(jù)小的情況。

其實(shí)spawn也可以通過配置shell option的方式來創(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);

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

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的話就會覆蓋掉默認(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)程斷開連接

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

// timer.js
while(true) {

}

但是主進(jìn)程并不需要長時間運(yùn)行的話就可以用detached來斷開二者之間的連接

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方法時,同時配置子進(jìn)程的stdio為ignore時,父進(jìn)程就可以獨(dú)立退出了

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

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

注意:fork方式創(chuàng)建的子進(jìn)程與父進(jìn)程是完全獨(dú)立的,它擁有多帶帶的內(nèi)存,多帶帶的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 }

回到本文初的那個問題,我們就可以將密集計算的邏輯放到多帶帶的js文件中,然后再通過fork的方式來計算,等計算完成時再通知主進(jìn)程計算結(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)聽進(jìn)程事件

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

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

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

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

先看一個正常退出的例子

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

再看一個因?yàn)榻邮盏絪ignal而終止的例子,應(yīng)用之前的timer文件,使用exec執(zhí)行的時候并指定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超時的時候error事件并不會觸發(fā),并且當(dāng)error事件觸發(fā)時exit事件并不一定會被觸發(fā)

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

無法創(chuàng)建進(jìn)程

無法結(jié)束進(jìn)程

給進(jìn)程發(fā)送消息失敗

注意當(dāng)代碼執(zhí)行出錯的時候,error事件并不會觸發(fā),exit事件會觸發(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通信管道的時候的信息傳遞,傳遞的過程中會經(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并不會被子進(jìn)程接收到

const { fork } = require("child_process");

const forked = fork("child.js");

forked.send({
  cmd: "NODE_foo",
  hello: "world"
});

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

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

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

參考資料

https://medium.freecodecamp.o...

https://nodejs.org/dist/lates...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107877.html

相關(guān)文章

  • Node.js process模塊解讀

    摘要:默認(rèn)情況下,會打印堆棧信息到然后退出進(jìn)程。適用于父子進(jìn)程之間發(fā)送消息,關(guān)于如何創(chuàng)建父子進(jìn)程會放在模塊中進(jìn)行。信號雖然也是用于請求終止進(jìn)程,但是它與有所不同,進(jìn)程可以選擇響應(yīng)還是忽略此信號。 process存在于全局對象上,不需要使用require()加載即可使用,process模塊主要做兩方面的事情 讀:獲取進(jìn)程信息(資源使用、運(yùn)行環(huán)境、運(yùn)行狀態(tài)) 寫:執(zhí)行進(jìn)程操作(監(jiān)聽事件、調(diào)度任...

    Riddler 評論0 收藏0
  • 通過源碼解析 Node.js 中進(jìn)程間通信中的 socket 句柄傳遞

    摘要:子進(jìn)程使用反序列化消息字符串為消息對象。在調(diào)用這類方法時,遍歷列表中的實(shí)例發(fā)送內(nèi)部消息,子進(jìn)程列表中的對應(yīng)項收到內(nèi)部消息并處理返回,父進(jìn)程中再結(jié)合返回結(jié)果和對應(yīng)著這個類實(shí)例維護(hù)的信息,保證功能的正確性。 在 Node.js 中,當(dāng)我們使用 child_process 模塊創(chuàng)建子進(jìn)程后,會返回一個 ChildProcess 類的實(shí)例,通過調(diào)用 ChildProcess#send(mess...

    HackerShell 評論0 收藏0
  • 深入理解Node.js 進(jìn)程與線程(8000長文徹底搞懂)

    摘要:在單核系統(tǒng)之上我們采用單進(jìn)程單線程的模式來開發(fā)。由進(jìn)程來管理所有的子進(jìn)程,主進(jìn)程不負(fù)責(zé)具體的任務(wù)處理,主要工作是負(fù)責(zé)調(diào)度和管理。模塊與模塊總結(jié)無論是模塊還是模塊,為了解決實(shí)例單線程運(yùn)行,無法利用多核的問題而出現(xiàn)的。 前言 進(jìn)程與線程是一個程序員的必知概念,面試經(jīng)常被問及,但是一些文章內(nèi)容只是講講理論知識,可能一些小伙伴并沒有真的理解,在實(shí)際開發(fā)中應(yīng)用也比較少。本篇文章除了介紹概念,通過...

    Harpsichord1207 評論0 收藏0
  • Node模塊--child_process

    摘要:說明模塊是的原始模塊主要作用執(zhí)行命令行命令該模塊的功能主要由函數(shù)提供區(qū)分和執(zhí)行命令第一個參數(shù)是將要執(zhí)行的命令,命令之間的參數(shù)使用空格分開第二個參數(shù)是回調(diào)函數(shù),有三個參數(shù)回調(diào)中的第一個參數(shù)命令執(zhí)行錯誤會有值,否則為回調(diào)中的第二個參數(shù)子進(jìn)程 1.說明 child_process 模塊是 Node.js 的原始模塊: 主要作用:執(zhí)行命令行命令 該模塊的功能主要由 child_process...

    ormsf 評論0 收藏0
  • 初識Node.js

    摘要:一旦替換已經(jīng)完成,該模塊將被完全棄用。用作錯誤處理事件文件,由在標(biāo)準(zhǔn)功能上的簡單包裝器提供所有模塊都提供這些對象。 Node.js簡介 Node 定義 Node.js是一個建立在Chrome v8 引擎上的javascript運(yùn)行時環(huán)境 Node 特點(diǎn) 異步事件驅(qū)動 showImg(https://segmentfault.com/img/bVMLD1?w=600&h=237); no...

    lk20150415 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<