摘要:最后发现使用子进程打开还真的就是使用到一定程度就挂掉。上面的简单流程就是启动一个子进程。逻辑就是,记录子进程的大小,一旦超过就掉子进程。我们在使用时,不知道设置,默认的是当我们子进程日志达到时,自动掉了。
如何在项目中实现热更新中提到的一个坑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/
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78332.html
摘要:前言记录下开发的一些事,加强自己对的应用。雏形参考于的项目管理,发现非常的优雅。尝试通过修改权限为,最后无果不了了之。方式二询问筛选会在终端监听输入的关键字,根据关键字筛选出一系列的仓库。自己也在使用,打算长期维护。 前言 记录下开发的一些事,加强自己对nodejs的应用。共勉! 有让你操蛋的事,就有需求 对于经常参与开源贡献,或者看见某些库,像试试手的人来说,经常需要git clon...
摘要:返回值对象利用给定的命令以及参数执行一个新的进程,如果没有参数数组,那么将默认是一个空数组。当子进程执行完毕后将会执行的回调函数,参数有返回值对象在中运行一个命令,并缓存命令的输出。 前言 众所周知,Node.js在child_process模块中提供了spawn和exec这两个方法,用来开启子进程执行指定程序。这两个方法虽然目的一样,但是既然Node.js为我们提供了两个方法,那它...
摘要:而且方式创建的子进程与父进程之间建立了通信管道,因此子进程和父进程之间可以通过的方式发送消息。与事件的回调函数有两个参数和,代码子进程最终的退出码,如果子进程是由于接收到信号终止的话,会记录子进程接受的值。 在介绍child_process模块之前,先来看一个下面的代码。 const http = require(http); const longComputation = () =>...
阅读 2542·2023-04-26 00:56
阅读 1999·2021-10-25 09:46
阅读 1235·2019-10-29 15:13
阅读 811·2019-08-30 15:54
阅读 2190·2019-08-29 17:10
阅读 2609·2019-08-29 15:43
阅读 495·2019-08-29 15:28
阅读 3022·2019-08-29 13:24