如何在項目中實現熱更新中提到的一個坑child_process的exec使用問題,下面文章會詳細介紹下,debug到node源碼中的詳細介紹,不容錯過。html
Nodejs是單線程單進程的,可是有了child_process模塊,能夠在程序中直接建立子進程,並使用主進程和子進程之間實現通訊。 node
對於child_process的使用,你們能夠找找其餘文章,介紹仍是比較多的,本文主要講一下踩過的坑。linux
在使用EHU(esl-hot-update)這個工具時(對於工具的介紹,參考前面的文章如何在項目中實現熱更新),發現用子進程啓動項目,常常性的掛掉。而後也不知道爲何,甚至懷疑子進程的效率比較低。 git
最後爲了進一步驗證,在一樣的環境下,一個直接啓動服務,一個是使用require('child_process').exec('...')
方式啓動。 github
最後發現使用子進程打開還真的就是使用到必定程度就掛掉。雖然此時也沒有什麼解決方案,可是至少能把問題定位在子進程上了,而不是其餘工具代碼致使程序掛掉。web
定位了問題後,網上查找child_process相關資料,發現exec與spawn方法的區別與陷阱 這篇文章提到幾點:windows
exec與spawn是有區別的微信
exec是對spawn的一個封裝app
最重要的exec比spawn多了一些默認的option函數
基於以上幾點有些頭緒了,可是仍是沒有明確的解決方案。
最後一個辦法,直接斷點到nodejs的child_process.js模塊中嘗試看看問題出在哪裏。
斷點進去看後,豁然開朗,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
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中第一次遇到。
感覺比較多的就是有時候正對問題去學習/研究,其實效率特別高。