深入淺析Node.js中建立子進程的方法
這篇文章帶大家了解一下Node.js中的子進程,介紹Node.js中創建子進程的四種方法,希望對大家有幫助!
眾所周知,Node.js 是單執行緒、非同步非阻塞的程式語言,那該如何充分利用多核心 CPU 的優勢呢?這就需要用到child_process 模組來建立子進程了,在Node.js 中,有四種方法可以建立子進程:
##exec
execFile
#spawn
- ##fork
實例(繼承自EventEmitter
),此實例有三個標準的 stdio 串流:
- ##child.stdin
-
-
子程序生命週期內可以註冊監聽的事件有:
exit
:子程序結束時觸發,參數為code 錯誤碼和signal 中斷訊號。close
exit 事件。
disconnect
child.disconnect() 或子程序呼叫
process.disconnect() 時觸發。
error
message
process.send() 發送訊息時觸發。
spawn
而
exec
execFile 方法也額外提供了一個回呼函數,會在子程序終止的時候觸發。接下來進行詳細分析:
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 函數的參數,會面臨指令列注入的風險,例如: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}`) })
find . -type f | wc -l; rm -rf /;
const { execFile } = require("child_process")
const child = execFile("node", ["--version"], (error, stdout, stderr) => {
if (error) throw error
console.log(stdout)
})
登入後複製
由於沒有建立shell,程式的參數會作為陣列傳入,因此具有較高的安全性。 spawnspawn 函數和 execFile 類似,預設不會開啟shell,但差異在於execFile 會快取命令列的輸出,然後把結果傳入回呼函數中,而spawn 則是以流的方式輸出,有了流,就能非常方便的對接輸入和輸出了,例如典型的const { execFile } = require("child_process") const child = execFile("node", ["--version"], (error, stdout, stderr) => { if (error) throw error console.log(stdout) })
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 是Word Count 的縮寫,用於統計單字數,語法為:
wc [OPTION]... [FILE]...
如果在終端機上輸入wc 指令並回車,這時候統計的是從鍵盤輸入終端機中的字符,再次按回車鍵,然後按在Node.js 中的寫法和命令列一模一樣:Ctrl D
會輸出統計的結果。
透過管道還可以組合複雜的命令,例如統計當前目錄下的檔案數量,在Linux 命令列中會這麼寫:
find . -type f | wc -l登入後複製
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}`) })
const child = spawn("find . -type f | wc -l", { stdio: "inherit", // 继承父进程的输入输出流 shell: true, // 开启命令行模式 cwd: "/Users/keliq/code", // 指定执行目录 env: { ANSWER: 42 }, // 指定环境变量(默认是 process.env) detached: true, // 作为独立进程存在 })
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 程式碼:const { fork } = require("child_process") const forked = fork("./child.js") forked.on("message", msg => { console.log("Message from child", msg); }) forked.send({ hello: "world" })
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中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

基於無阻塞、事件驅動建立的Node服務,具有記憶體消耗低的優點,非常適合處理海量的網路請求。在海量請求的前提下,就需要考慮「記憶體控制」的相關問題了。 1. V8的垃圾回收機制與記憶體限制 Js由垃圾回收機

選擇一個Node的Docker映像看起來像是小事,但是映像的大小和潛在漏洞可能會對你的CI/CD流程和安全造成重大的影響。那我們要如何選擇一個最好Node.js Docker映像呢?

Node 19已正式發布,以下這篇文章就來帶大家詳解了解Node.js 19的 6 大特性,希望對大家有幫助!

文件模組是對底層文件操作的封裝,例如文件讀寫/打開關閉/刪除添加等等文件模組最大的特點就是所有的方法都提供的**同步**和**異步**兩個版本,具有sync 字尾的方法都是同步方法,沒有的都是異

事件循環是 Node.js 的基本組成部分,透過確保主執行緒不被阻塞來實現非同步編程,了解事件循環對建立高效應用程式至關重要。以下這篇文章就來帶大家深入了解Node中的事件循環 ,希望對大家有幫助!

如何用pkg打包nodejs可執行檔?以下這篇文章跟大家介紹一下使用pkg將Node專案打包為執行檔的方法,希望對大家有幫助!

node無法用npm指令是因為沒有正確配置環境變量,其解決方法是:1、開啟“系統屬性”;2、找到“環境變數”->“系統變數”,然後編輯環境變數;3、找到nodejs所在的資料夾;4、點選「確定」即可。
