摘要:最后發(fā)現(xiàn)使用子進(jìn)程打開還真的就是使用到一定程度就掛掉。上面的簡(jiǎn)單流程就是啟動(dòng)一個(gè)子進(jìn)程。邏輯就是,記錄子進(jìn)程的大小,一旦超過就掉子進(jìn)程。我們?cè)谑褂脮r(shí),不知道設(shè)置,默認(rèn)的是當(dāng)我們子進(jìn)程日志達(dá)到時(shí),自動(dòng)掉了。
如何在項(xiàng)目中實(shí)現(xiàn)熱更新中提到的一個(gè)坑child_process的exec使用問題,下面文章會(huì)詳細(xì)介紹下,debug到node源碼中的詳細(xì)介紹,不容錯(cuò)過。
child_process介紹Nodejs是單線程單進(jìn)程的,但是有了child_process模塊,可以在程序中直接創(chuàng)建子進(jìn)程,并使用主進(jìn)程和子進(jìn)程之間實(shí)現(xiàn)通信。
對(duì)于child_process的使用,大家可以找找其他文章,介紹還是比較多的,本文主要講一下踩過的坑。
踩過的坑在使用EHU(esl-hot-update)這個(gè)工具時(shí)(對(duì)于工具的介紹,參考前面的文章如何在項(xiàng)目中實(shí)現(xiàn)熱更新),發(fā)現(xiàn)用子進(jìn)程啟動(dòng)項(xiàng)目,經(jīng)常性的掛掉。然后也不知道為什么,甚至懷疑子進(jìn)程的效率比較低。
最后為了進(jìn)一步驗(yàn)證,在同樣的環(huán)境下,一個(gè)直接啟動(dòng)服務(wù),一個(gè)是使用require("child_process").exec("...") 方式啟動(dòng)。
最后發(fā)現(xiàn)使用子進(jìn)程打開還真的就是使用到一定程度就掛掉。雖然此時(shí)也沒有什么解決方案,但是至少能把問題定位在子進(jìn)程上了,而不是其他工具代碼導(dǎo)致程序掛掉。
定位問題定位了問題后,網(wǎng)上查找child_process相關(guān)資料,發(fā)現(xiàn)exec與spawn方法的區(qū)別與陷阱 這篇文章提到幾點(diǎn):
exec與spawn是有區(qū)別的
exec是對(duì)spawn的一個(gè)封裝
最重要的exec比spawn多了一些默認(rèn)的option
基于以上幾點(diǎn)有些頭緒了,但是還是沒有明確的解決方案。
最后一個(gè)辦法,直接斷點(diǎn)到nodejs的child_process.js模塊中嘗試看看問題出在哪里。
exec和spawn的源碼區(qū)分斷點(diǎn)進(jìn)去看后,豁然開朗,exec是對(duì)execFile的封裝,execFile又是對(duì)spawn 的封裝。
每一層封裝都是加強(qiáng)一些易用性以及功能。
直接看源碼:
exports.exec = function(command /*, options, callback*/) { var opts = normalizeExecArgs.apply(null, arguments); return exports.execFile(opts.file, opts.args, opts.options, opts.callback); };
exec對(duì)于execFile的封裝是進(jìn)行參數(shù)處理
處理的函數(shù):
normalizeExecArgs
關(guān)鍵邏輯
if (process.platform === "win32") { file = process.env.comspec || "cmd.exe"; args = ["/s", "/c", """ + command + """]; // Make a shallow copy before patching so we don"t clobber the user"s // options object. options = util._extend({}, options); options.windowsVerbatimArguments = true; } else { file = "/bin/sh"; args = ["-c", command]; }
將簡(jiǎn)單的command命名做一個(gè),win和linux的平臺(tái)處理。
此時(shí)execFile接受到的就是一個(gè)區(qū)分平臺(tái)的command參數(shù)。
然后重點(diǎn)來了,繼續(xù)debug,execFile中:
var options = { encoding: "utf8", timeout: 0, maxBuffer: 200 * 1024, killSignal: "SIGTERM", cwd: null, env: null };
有這么一段,設(shè)置了默認(rèn)的參數(shù)。然后后面又是一些參數(shù)處理,最后調(diào)用spawn方法啟動(dòng)子進(jìn)程。
上面的簡(jiǎn)單流程就是啟動(dòng)一個(gè)子進(jìn)程。到這里都沒有什么問題。
繼續(xù)看,重點(diǎn)又來了:
用過子進(jìn)程應(yīng)該知道這個(gè)child.stderr
下面的代碼就解答了為什么子進(jìn)程會(huì)掛掉。
child.stderr.addListener("data", function(chunk) { stderrLen += chunk.length; if (stderrLen > options.maxBuffer) { ex = new Error("stderr maxBuffer exceeded."); kill(); } else { if (!encoding) _stderr.push(chunk); else _stderr += chunk; } });
邏輯就是,記錄子進(jìn)程的log大小,一旦超過maxBuffer就kill掉子進(jìn)程。
原來真相在這里。我們?cè)谑褂?b>exec時(shí),不知道設(shè)置maxBuffer,默認(rèn)的maxBuffer是200K,當(dāng)我們子進(jìn)程日志達(dá)到200K時(shí),自動(dòng)kill()掉了。
exec和spawn的使用區(qū)分不過exec確實(shí)比spawn在使用上面要好很多
例如我們執(zhí)行一個(gè)命令
使用exec
require("child_process").exec("edp webserver start");
使用spawn
linux下這么搞
var child = require("child_process").spawn( "/bin/sh", ["-c","edp webserver start"], { cwd: null, env: null, windowsVerbatimArguments: false } );
win下
var child = require("child_process").spawn( "cmd.exe", ["/s", "/c", "edp webserver start"], { cwd: null, env: null, windowsVerbatimArguments: true } );
可見spawn還是比較麻煩的。
解決方案知道上面原因了,解決方案就有幾個(gè)了:
子進(jìn)程的系統(tǒng),不再輸出日志
maxBuffer這個(gè)傳一個(gè)足夠大的參數(shù)
直接使用spawn,放棄使用exec
我覺得最優(yōu)的方案是直接使用spawn,解除maxBuffer的限制。但是實(shí)際處理中,發(fā)現(xiàn)直接考出normalizeExecArgs這個(gè)方法去處理平臺(tái)問題,在win下還是有些不好用,mac下沒有問題。所以暫時(shí)將maxBuffer設(shè)置了一個(gè)極大值,保證大家的正常使用。然后后續(xù)在優(yōu)化成spawn方法。
吐槽其實(shí)沒有怎么理解,execFile對(duì)于spawn封裝加maxBuffer的這個(gè)邏輯,而且感覺就算加了,是否也可以給一個(gè)方式,去掉maxBuffer的限制。
難道是子進(jìn)程的log量會(huì)影響性能?
感想其實(shí)在解決這個(gè)問題時(shí),發(fā)現(xiàn)這個(gè)差異/坑還比較意外,因?yàn)樽陨韺?duì)于node其實(shí)還不是很熟,這個(gè)子進(jìn)程的使用其實(shí)也是在ehu中第一次遇到。
感受比較多的就是有時(shí)候正對(duì)問題去學(xué)習(xí)/研究,其實(shí)效率特別高。
微信公眾號(hào) 博客地址http://tangguangyao.github.io/
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78332.html
摘要:前言記錄下開發(fā)的一些事,加強(qiáng)自己對(duì)的應(yīng)用。雛形參考于的項(xiàng)目管理,發(fā)現(xiàn)非常的優(yōu)雅。嘗試通過修改權(quán)限為,最后無果不了了之。方式二詢問篩選會(huì)在終端監(jiān)聽輸入的關(guān)鍵字,根據(jù)關(guān)鍵字篩選出一系列的倉(cāng)庫(kù)。自己也在使用,打算長(zhǎng)期維護(hù)。 前言 記錄下開發(fā)的一些事,加強(qiáng)自己對(duì)nodejs的應(yīng)用。共勉! 有讓你操蛋的事,就有需求 對(duì)于經(jīng)常參與開源貢獻(xiàn),或者看見某些庫(kù),像試試手的人來說,經(jīng)常需要git clon...
摘要:返回值對(duì)象利用給定的命令以及參數(shù)執(zhí)行一個(gè)新的進(jìn)程,如果沒有參數(shù)數(shù)組,那么將默認(rèn)是一個(gè)空數(shù)組。當(dāng)子進(jìn)程執(zhí)行完畢后將會(huì)執(zhí)行的回調(diào)函數(shù),參數(shù)有返回值對(duì)象在中運(yùn)行一個(gè)命令,并緩存命令的輸出。 前言 眾所周知,Node.js在child_process模塊中提供了spawn和exec這兩個(gè)方法,用來開啟子進(jìn)程執(zhí)行指定程序。這兩個(gè)方法雖然目的一樣,但是既然Node.js為我們提供了兩個(gè)方法,那它...
摘要:通過將的給出來的進(jìn)程。恩吞吐率關(guān)于吞吐率有多種解讀,一種是描繪服務(wù)器單位時(shí)間處理請(qǐng)求的能力。而根據(jù)這個(gè)描述的話他的單位就為而這個(gè)指標(biāo)就是上面數(shù)據(jù)中的當(dāng)然,肯定是越大越好了吞吐量這個(gè)和上面的吞吐率很有點(diǎn)關(guān)系的。 首先鄭重聲明:nodeJS 是一門單線程!異步!非阻塞語言!nodeJS 是一門單線程!異步!非阻塞語言!nodeJS 是一門單線程!異步!非阻塞語言! 重要的事情說3遍。 因?yàn)?..
摘要:通過將的給出來的進(jìn)程。恩吞吐率關(guān)于吞吐率有多種解讀,一種是描繪服務(wù)器單位時(shí)間處理請(qǐng)求的能力。而根據(jù)這個(gè)描述的話他的單位就為而這個(gè)指標(biāo)就是上面數(shù)據(jù)中的當(dāng)然,肯定是越大越好了吞吐量這個(gè)和上面的吞吐率很有點(diǎn)關(guān)系的。 首先鄭重聲明:nodeJS 是一門單線程!異步!非阻塞語言!nodeJS 是一門單線程!異步!非阻塞語言!nodeJS 是一門單線程!異步!非阻塞語言! 重要的事情說3遍。 因?yàn)?..
摘要:而且方式創(chuàng)建的子進(jìn)程與父進(jìn)程之間建立了通信管道,因此子進(jìn)程和父進(jìn)程之間可以通過的方式發(fā)送消息。與事件的回調(diào)函數(shù)有兩個(gè)參數(shù)和,代碼子進(jìn)程最終的退出碼,如果子進(jìn)程是由于接收到信號(hào)終止的話,會(huì)記錄子進(jìn)程接受的值。 在介紹child_process模塊之前,先來看一個(gè)下面的代碼。 const http = require(http); const longComputation = () =>...
閱讀 2593·2023-04-26 00:56
閱讀 2037·2021-10-25 09:46
閱讀 1276·2019-10-29 15:13
閱讀 839·2019-08-30 15:54
閱讀 2224·2019-08-29 17:10
閱讀 2641·2019-08-29 15:43
閱讀 517·2019-08-29 15:28
閱讀 3058·2019-08-29 13:24