搜尋
首頁web前端js教程深入淺析Node.js中建立子進程的方法

深入淺析Node.js中建立子進程的方法

Oct 12, 2021 am 10:05 AM
node.js子行程

這篇文章帶大家了解一下Node.js中的子進程,介紹Node.js中創建子進程的四種方法,希望對大家有幫助!

深入淺析Node.js中建立子進程的方法

眾所周知,Node.js 是單執行緒、非同步非阻塞的程式語言,那該如何充分利用多核心 CPU 的優勢呢?這就需要用到child_process 模組來建立子進程了,在Node.js 中,有四種方法可以建立子進程:

  • ##exec

  • execFile

  • #spawn

  • ##fork

  • 【推薦學習:《
nodejs 教學

》】上面四個方法都會回傳

ChildProcess

實例(繼承自EventEmitter),此實例有三個標準的 stdio 串流:

    ##child.stdin
  • child.stdout
  • fchild.stderr
  • 子程序生命週期內可以註冊監聽的事件有:

exit

:子程序結束時觸發,參數為code 錯誤碼和signal 中斷訊號。

close

:子程序結束並且 stdio 流被關閉時觸發,參數同

exit 事件。 disconnect

:父程序呼叫

child.disconnect() 或子程序呼叫 process.disconnect() 時觸發。 error

:子進程無法建立、或無法被殺、或發送訊息給子進程失敗時觸發。

message

:子進程透過

process.send() 發送訊息時觸發。 spawn

:當子程序建立成功時觸發(Node.js v15.1版本才加入此事件)。

exec

execFile 方法也額外提供了一個回呼函數,會在子程序終止的時候觸發。接下來進行詳細分析:exec

exec 方法用於執行 bash 命令,它的參數是一個命令字串。例如統計目前目錄下的檔案數量,用 exec 函數的寫法為:

const { exec } = require("child_process")
exec("find . -type f | wc -l", (err, stdout, stderr) => {
  if (err) return console.error(`exec error: ${err}`)
  console.log(`Number of files ${stdout}`)
})

exec 會新建一個子進程,然後快取它的運作結果,執行結束後呼叫回呼函數。

可能你已經想到了,exec 指令是比較危險的,假如把使用者提供的字串當作exec 函數的參數,會面臨指令列注入的風險,例如:

find . -type f | wc -l; rm -rf /;

另外,由於exec 會在記憶體中快取全部的輸出結果,當資料比較大的時候,spawn 會是更好的選擇。

execFile

execFile 和exec 的差別在於它並不會建立shell,而是直接執行指令,所以會更有效率一點,例如:

const { execFile } = require("child_process")
const child = execFile("node", ["--version"], (error, stdout, stderr) => {
  if (error) throw error
  console.log(stdout)
})

由於沒有建立shell,程式的參數會作為陣列傳入,因此具有較高的安全性。

spawn

spawn 函數和 execFile 類似,預設不會開啟shell,但差異在於execFile 會快取命令列的輸出,然後把結果傳入回呼函數中,而spawn 則是以流的方式輸出,有了流,就能非常方便的對接輸入和輸出了,例如典型的

wc

命令:

const child = spawn("wc")
process.stdin.pipe(child.stdin)
child.stdout.on("data", data => {
  console.log(`child stdout:\n${data}`)
})
此時就會從命令列stdin 獲取輸入,當使用者觸發回車ctrl D

時就開始執行指令,並把結果從stdout 輸出。

wc 是Wo​​rd Count 的縮寫,用於統計單字數,語法為:

wc [OPTION]... [FILE]...
如果在終端機上輸入wc 指令並回車,這時候統計的是從鍵盤輸入終端機中的字符,再次按回車鍵,然後按

Ctrl D

會輸出統計的結果。

透過管道還可以組合複雜的命令,例如統計當前目錄下的檔案數量,在Linux 命令列中會這麼寫:

find . -type f | wc -l
在Node.js 中的寫法和命令列一模一樣:

const find = spawn("find", [".", "-type", "f"])
const wc = spawn("wc", ["-l"])
find.stdout.pipe(wc.stdin)
wc.stdout.on("data", (data) => {
  console.log(`Number of files ${data}`)
})

spawn 有豐富的自訂配置,例如:

const child = spawn("find . -type f | wc -l", {
  stdio: "inherit", // 继承父进程的输入输出流
  shell: true, // 开启命令行模式
  cwd: "/Users/keliq/code", // 指定执行目录
  env: { ANSWER: 42 }, // 指定环境变量(默认是 process.env)
  detached: true, // 作为独立进程存在
})

fork

fork 函數是spawn 函數的變體,使用fork 建立的子行程和父行程之間會自動建立一個通訊通道,子行程的全域物件process 上面會掛載send 方法。例如父進程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)

當呼叫

fork("child.js")

的時候,實際上就是用node 來執行該檔案中的程式碼,相當於

spawn('node', ['./child.js'])fork 的一個典型的應用場景如下:假如現在用 Node.js 建立一個 http 服務,當路由為 compute

的時候,執行一個耗時的運算。

const http = require("http")
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);
可以用下面的程式碼來模擬該耗時的運算:
const longComputation = () => {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) {
    sum += i
  }
  return sum
}

那么在上线后,只要服务端收到了 compute 请求,由于 Node.js 是单线程的,耗时运算占用了 CPU,用户的其他请求都会阻塞在这里,表现出来的现象就是服务器无响应。

解决这个问题最简单的方法就是把耗时运算放到子进程中去处理,例如创建一个 compute.js 的文件,代码如下:

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

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

再把服务端的代码稍作改造:

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)

这样的话,主线程就不会阻塞,而是继续处理其他的请求,当耗时运算的结果返回后,再做出响应。其实更简单的处理方式是利用 cluster 模块,限于篇幅原因,后面再展开讲。

总结

掌握了上面四种创建子进程的方法之后,总结了以下三条规律:

  • 创建 node 子进程用 fork,因为自带通道方便通信。
  • 创建非 node 子进程用 execFile 或 spawn。如果输出内容较少用 execFile,会缓存结果并传给回调方便处理;如果输出内容多用 spawn,使用流的方式不会占用大量内存。
  • 执行复杂的、固定的终端命令用 exec,写起来更方便。但一定要记住 exec 会创建 shell,效率不如 execFile 和 spawn,且存在命令行注入的风险。

更多编程相关知识,请访问:编程视频!!

以上是深入淺析Node.js中建立子進程的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器