摘要:最后發現使用子進程打開還真的就是使用到一定程度就掛掉。上面的簡單流程就是啟動一個子進程。邏輯就是,記錄子進程的大小,一旦超過就掉子進程。我們在使用時,不知道設置,默認的是當我們子進程日志達到時,自動掉了。
如何在項目中實現熱更新中提到的一個坑child_process的exec使用問題,下面文章會詳細介紹下,debug到node源碼中的詳細介紹,不容錯過。
child_process介紹Nodejs是單線程單進程的,但是有了child_process模塊,可以在程序中直接創建子進程,并使用主進程和子進程之間實現通信。
對于child_process的使用,大家可以找找其他文章,介紹還是比較多的,本文主要講一下踩過的坑。
踩過的坑在使用EHU(esl-hot-update)這個工具時(對于工具的介紹,參考前面的文章如何在項目中實現熱更新),發現用子進程啟動項目,經常性的掛掉。然后也不知道為什么,甚至懷疑子進程的效率比較低。
最后為了進一步驗證,在同樣的環境下,一個直接啟動服務,一個是使用require("child_process").exec("...") 方式啟動。
最后發現使用子進程打開還真的就是使用到一定程度就掛掉。雖然此時也沒有什么解決方案,但是至少能把問題定位在子進程上了,而不是其他工具代碼導致程序掛掉。
定位問題定位了問題后,網上查找child_process相關資料,發現exec與spawn方法的區別與陷阱 這篇文章提到幾點:
exec與spawn是有區別的
exec是對spawn的一個封裝
最重要的exec比spawn多了一些默認的option
基于以上幾點有些頭緒了,但是還是沒有明確的解決方案。
最后一個辦法,直接斷點到nodejs的child_process.js模塊中嘗試看看問題出在哪里。
exec和spawn的源碼區分斷點進去看后,豁然開朗,exec是對execFile的封裝,execFile又是對spawn 的封裝。
每一層封裝都是加強一些易用性以及功能。
直接看源碼:
exports.exec = function(command /*, options, callback*/) { var opts = normalizeExecArgs.apply(null, arguments); return exports.execFile(opts.file, opts.args, opts.options, opts.callback); };
exec對于execFile的封裝是進行參數處理
處理的函數:
normalizeExecArgs
關鍵邏輯
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]; }
將簡單的command命名做一個,win和linux的平臺處理。
此時execFile接受到的就是一個區分平臺的command參數。
然后重點來了,繼續debug,execFile中:
var options = { encoding: "utf8", timeout: 0, maxBuffer: 200 * 1024, killSignal: "SIGTERM", cwd: null, env: null };
有這么一段,設置了默認的參數。然后后面又是一些參數處理,最后調用spawn方法啟動子進程。
上面的簡單流程就是啟動一個子進程。到這里都沒有什么問題。
繼續看,重點又來了:
用過子進程應該知道這個child.stderr
下面的代碼就解答了為什么子進程會掛掉。
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; } });
邏輯就是,記錄子進程的log大小,一旦超過maxBuffer就kill掉子進程。
原來真相在這里。我們在使用exec時,不知道設置maxBuffer,默認的maxBuffer是200K,當我們子進程日志達到200K時,自動kill()掉了。
exec和spawn的使用區分不過exec確實比spawn在使用上面要好很多
例如我們執行一個命令
使用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還是比較麻煩的。
解決方案知道上面原因了,解決方案就有幾個了:
子進程的系統,不再輸出日志
maxBuffer這個傳一個足夠大的參數
直接使用spawn,放棄使用exec
我覺得最優的方案是直接使用spawn,解除maxBuffer的限制。但是實際處理中,發現直接考出normalizeExecArgs這個方法去處理平臺問題,在win下還是有些不好用,mac下沒有問題。所以暫時將maxBuffer設置了一個極大值,保證大家的正常使用。然后后續在優化成spawn方法。
吐槽其實沒有怎么理解,execFile對于spawn封裝加maxBuffer的這個邏輯,而且感覺就算加了,是否也可以給一個方式,去掉maxBuffer的限制。
難道是子進程的log量會影響性能?
感想其實在解決這個問題時,發現這個差異/坑還比較意外,因為自身對于node其實還不是很熟,這個子進程的使用其實也是在ehu中第一次遇到。
感受比較多的就是有時候正對問題去學習/研究,其實效率特別高。
微信公眾號 博客地址http://tangguangyao.github.io/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78332.html
摘要:前言記錄下開發的一些事,加強自己對的應用。雛形參考于的項目管理,發現非常的優雅。嘗試通過修改權限為,最后無果不了了之。方式二詢問篩選會在終端監聽輸入的關鍵字,根據關鍵字篩選出一系列的倉庫。自己也在使用,打算長期維護。 前言 記錄下開發的一些事,加強自己對nodejs的應用。共勉! 有讓你操蛋的事,就有需求 對于經常參與開源貢獻,或者看見某些庫,像試試手的人來說,經常需要git clon...
摘要:返回值對象利用給定的命令以及參數執行一個新的進程,如果沒有參數數組,那么將默認是一個空數組。當子進程執行完畢后將會執行的回調函數,參數有返回值對象在中運行一個命令,并緩存命令的輸出。 前言 眾所周知,Node.js在child_process模塊中提供了spawn和exec這兩個方法,用來開啟子進程執行指定程序。這兩個方法雖然目的一樣,但是既然Node.js為我們提供了兩個方法,那它...
摘要:而且方式創建的子進程與父進程之間建立了通信管道,因此子進程和父進程之間可以通過的方式發送消息。與事件的回調函數有兩個參數和,代碼子進程最終的退出碼,如果子進程是由于接收到信號終止的話,會記錄子進程接受的值。 在介紹child_process模塊之前,先來看一個下面的代碼。 const http = require(http); const longComputation = () =>...
閱讀 2543·2023-04-26 00:56
閱讀 2000·2021-10-25 09:46
閱讀 1236·2019-10-29 15:13
閱讀 811·2019-08-30 15:54
閱讀 2190·2019-08-29 17:10
閱讀 2612·2019-08-29 15:43
閱讀 497·2019-08-29 15:28
閱讀 3022·2019-08-29 13:24