child_process
Nodejs是单线程单进程的,但是有了child_process模块,可以在程序中直接创建子进程,并使用主进程和子进程之间实现通信。
问题
用child_process.exec启动的项目经常性的挂掉。
原因定位:
exec与spawn是有区别的
exec是对spawn的一个封装
最重要的exec比spawn多了一些默认的option
exec和spawn的源码区分
exec是对execFile的封装,execFile又是对spawn的封装。
每一层封装都是加强一些易用性以及功能。
源码
1 | exports.exec = function(command /*, options, callback*/) { |
exec对于execFile的封装是进行参数处理, 处理的函数:normalizeExecArgs。
关键处理逻辑
1 | if (process.platform === 'win32') { |
将简单的command命名做一个,win和linux的平台处理。
此时execFile接受到的就是一个区分平台的command参数。
然后重点来了,继续debug,execFile中:
1 | var options = { |
有这么一段,设置了默认的参数。然后后面又是一些参数处理,最后调用spawn方法启动子进程。
上面的简单流程就是启动一个子进程。到这里都没有什么问题。
继续看,重点又来了:
用过子进程应该知道这个child.stderr
下面的代码就解答了为什么子进程会挂掉。
1 | child.stderr.addListener('data', function(chunk) { |
逻辑就是,记录子进程的log大小,一旦超过maxBuffer就kill掉子进程。
原来真相在这里。我们在使用exec时,不知道设置maxBuffer,默认的maxBuffer是200K,当我们子进程日志达到200K时,自动kill()掉了。
exec和spawn的使用区分
不过exec确实比spawn在使用上面要好很多
例如我们执行一个命令
使用exec
require(‘child_process’).exec(‘edp webserver start’);
使用spawn
linux下这么搞
1 | var child = require('child_process').spawn( |
win下
1 | var child = require('child_process').spawn( |
可见spawn还是比较麻烦的。
解决方案
知道上面原因了,解决方案就有几个了:
子进程的系统,不再输出日志
maxBuffer这个传一个足够大的参数
直接使用spawn,放弃使用exec
我觉得最优的方案是直接使用spawn,解除maxBuffer的限制。但是实际处理中,发现直接考出normalizeExecArgs这个方法去处理平台问题,在win下还是有些不好用,mac下没有问题。所以暂时将maxBuffer设置了一个极大值,保证大家的正常使用。然后后续在优化成spawn方法。