Node.js: managing child processes
2016-07-04 17:30
633 查看
http://krasimirtsonev.com/blog/article/Nodejs-managing-child-processes-starting-stopping-exec-spawn
/ blog / Nodejs
These days I spent some time working on Yez!. Chrome extension whose main role is to replace the annoying switching between the terminal and the browser.
It uses Node.js module to run shell commands. So, I had to deal with child processes, and I decided to document my experience.
Here is an example of using the first one:
We are passing our command node -v as a first argument and expect to get response in the provided callback. stdout is what we normally need. That's the output of the command. error should be null if everything is ok. Let's
see what will happen if we run the following failing script:
The result is:
stderr contains the text of the error and error is an instance of Error class. So we may use error.code or error.signal to get more information about the problem.
The above exec snippet works for short-time commands. Processes whose job is to return a response immediately. However, I'm planning to use Yez! for
much complex tasks like starting Gulp or Grunt tasks and monitoring their output. Or running a Node.js server and watching its logs. For example:
We run node ./script.js which internally starts our server. It listens on localhost, port 1337. In order to stop the server we use a counter visits. We should requesthttp://127.0.0.1:1337 several times and see the following response
in the terminal:
The problem is that we get the result when the process exits. The sentence saying that the server is running is sent to stdout stream, but because we have only one callback we get the result at the end. It's the same with the console.log(msg)messages.
It won't work for my use case.
So, the better approach here will be to attach listeners to the stdout and stderr. There is also a close event available. Let's change our script.js file to the following code, that illustrates the idea:
Now, it is much better because we get instant updates on what is going on. The output is:
There is an alternative of exec called spawn. Unlike exec it launches a new process and should be used if we expect long-time communication with the running command. The API is
a little bit different, but both methods work similar.
spawn doesn't accept a callback and if we need to pass arguments along with the command name we have to do this in an additional [args] array.
it worked with the simple scripts which I tested. The problem occurred when I launched grunt command. The Chrome extension nicely showed the results of the tasks but at some point I wanted to restart the process. And it looks like (even after the
usage of child.kill) I didn't get the close event firing. So I investigated a bit and saw that the child fired theexit event,
which for me means that the job is somehow 50% done. What was happening was that Grunt launches a bunch of other processes that were still active. In such situations, I normally start searching for a ready-to-use solution, and I found one. It was in the forever-monitor
module:
We are using the PID of the created child to find out its sub processes (via the ps-treedependency). So far so good. Everything works, but ... guess what ... only under Linux. Once I tried the solution on my Windows7 I got no results. The
tasks that I run were active no matter what.
And as it normally happens, few hours later I found a workaround. It's dummy, but it works. There is a Windows command line instrument called taskkill. So, what I have to do is to detect that my Node.js module is operating on a Windows machine and
send the PID to that small utility.
The /T (terminates all the sub processes) and /F (forcefully terminating) arguments are also critical and without them we can't achieve the desired results.
my dream for terminal in the Chrome's DevTools seems attainable.
****************************************************************************************************************************************************************************************
另外这边也有kill一个ps tree的包
https://github.com/pkrumins/node-tree-kill
Node.js: managing child processes
/ blog / NodejsThese days I spent some time working on Yez!. Chrome extension whose main role is to replace the annoying switching between the terminal and the browser.
It uses Node.js module to run shell commands. So, I had to deal with child processes, and I decided to document my experience.
Running shell command
There are two methods for running child processes under Node.js - exec and spawn.Here is an example of using the first one:
var exec = require('child_process').exec; exec('node -v', function(error, stdout, stderr) { console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); if (error !== null) { console.log('exec error: ' + error); } });
We are passing our command node -v as a first argument and expect to get response in the provided callback. stdout is what we normally need. That's the output of the command. error should be null if everything is ok. Let's
see what will happen if we run the following failing script:
// failing.js console.log('Hello!'); throw new Error('Ops!') console.log('End!'); // script.js var exec = require('child_process').exec; exec('node ./failing.js', function(error, stdout, stderr) { console.log('stdout: ', stdout); console.log('stderr: ', stderr); if (error !== null) { console.log('exec error: ', error); } });
The result is:
exec error: { [Error: Command failed: failing.js:2 throw new Error('Ops!') ^ Error: Ops! at Object.<anonymous> (failing.js:2:7) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:902:3 ] killed: false, code: 8, signal: null }
stderr contains the text of the error and error is an instance of Error class. So we may use error.code or error.signal to get more information about the problem.
The above exec snippet works for short-time commands. Processes whose job is to return a response immediately. However, I'm planning to use Yez! for
much complex tasks like starting Gulp or Grunt tasks and monitoring their output. Or running a Node.js server and watching its logs. For example:
// server.js var http = require('http'); var visits = 0; http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); visits += 1; var msg = 'Visits: ' + visits; res.end(msg + '\n'); console.log(msg); if(visits == 5) { process.exit(); } }).listen(1337, '127.0.0.1'); console.log('Server running at http://127.0.0.1:1337/'); // script.js var exec = require('child_process').exec; exec('node ./server.js', function(error, stdout, stderr) { console.log('stdout: ', stdout); console.log('stderr: ', stderr); if (error !== null) { console.log('exec error: ', error); } });
We run node ./script.js which internally starts our server. It listens on localhost, port 1337. In order to stop the server we use a counter visits. We should requesthttp://127.0.0.1:1337 several times and see the following response
in the terminal:
stdout: Server running at http://127.0.0.1:1337/ Visits: 1 Visits: 2 Visits: 3 Visits: 4 stderr:
The problem is that we get the result when the process exits. The sentence saying that the server is running is sent to stdout stream, but because we have only one callback we get the result at the end. It's the same with the console.log(msg)messages.
It won't work for my use case.
So, the better approach here will be to attach listeners to the stdout and stderr. There is also a close event available. Let's change our script.js file to the following code, that illustrates the idea:
var exec = require('child_process').exec; var child = exec('node ./commands/server.js'); child.stdout.on('data', function(data) { console.log('stdout: ' + data); }); child.stderr.on('data', function(data) { console.log('stdout: ' + data); }); child.on('close', function(code) { console.log('closing code: ' + code); });
Now, it is much better because we get instant updates on what is going on. The output is:
stdout: Server running at http://127.0.0.1:1337/ stdout: Visits: 1 stdout: Visits: 2 stdout: Visits: 3 stdout: Visits: 4 stdout: Visits: 5 closing code: 0
There is an alternative of exec called spawn. Unlike exec it launches a new process and should be used if we expect long-time communication with the running command. The API is
a little bit different, but both methods work similar.
child_process.exec(command, [options], callback) child_process.spawn(command, [args], [options])
spawn doesn't accept a callback and if we need to pass arguments along with the command name we have to do this in an additional [args] array.
Killing/Stopping the command
Okey, I didn't say anything new. There are tons of article about how to run a shell command from a Node.js script. However, there are just few resources explaining how to stop the running process. In the beginning I used child.kill andit worked with the simple scripts which I tested. The problem occurred when I launched grunt command. The Chrome extension nicely showed the results of the tasks but at some point I wanted to restart the process. And it looks like (even after the
usage of child.kill) I didn't get the close event firing. So I investigated a bit and saw that the child fired theexit event,
which for me means that the job is somehow 50% done. What was happening was that Grunt launches a bunch of other processes that were still active. In such situations, I normally start searching for a ready-to-use solution, and I found one. It was in the forever-monitor
module:
var psTree = require('ps-tree'); var kill = function (pid, signal, callback) { signal = signal || 'SIGKILL'; callback = callback || function () {}; var killTree = true; if(killTree) { psTree(pid, function (err, children) { [pid].concat( children.map(function (p) { return p.PID; }) ).forEach(function (tpid) { try { process.kill(tpid, signal) } catch (ex) { } }); callback(); }); } else { try { process.kill(pid, signal) } catch (ex) { } callback(); } }; // ... somewhere in the code of Yez! kill(child.pid);
We are using the PID of the created child to find out its sub processes (via the ps-treedependency). So far so good. Everything works, but ... guess what ... only under Linux. Once I tried the solution on my Windows7 I got no results. The
tasks that I run were active no matter what.
And as it normally happens, few hours later I found a workaround. It's dummy, but it works. There is a Windows command line instrument called taskkill. So, what I have to do is to detect that my Node.js module is operating on a Windows machine and
send the PID to that small utility.
var isWin = /^win/.test(process.platform); if(!isWin) { kill(processing.pid); } else { var cp = require('child_process'); cp.exec('taskkill /PID ' + processing.pid + ' /T /F', function (error, stdout, stderr) { // console.log('stdout: ' + stdout); // console.log('stderr: ' + stderr); // if(error !== null) { // console.log('exec error: ' + error); // } }); }
The /T (terminates all the sub processes) and /F (forcefully terminating) arguments are also critical and without them we can't achieve the desired results.
Summary
I was trying to do the same things like six or seven months back. The spawn process wasn't act as it acts now. Especially under Windows. Now, everything works smooth and as we saw with only one dirty hack we are able to execute shell commands. So,my dream for terminal in the Chrome's DevTools seems attainable.
****************************************************************************************************************************************************************************************
另外这边也有kill一个ps tree的包
https://github.com/pkrumins/node-tree-kill
相关文章推荐
- 使用ruby部署工具mina快速部署nodejs应用教程
- Google官方支持的NodeJS访问API,提供后台登录授权
- 浅谈Nodejs观察者模式
- nodejs教程之环境安装及运行
- nodejs中的fiber(纤程)库详解
- 基于NodeJS的前后端分离的思考与实践(五)多终端适配
- 基于NodeJS的前后端分离的思考与实践(二)模版探索
- 实例详解Nodejs 保存 payload 发送过来的文件
- 我的NodeJs学习小结(一)
- Node.js 应用跑得更快 10 个技巧
- nodejs中实现sleep功能实例
- Nodejs异步回调的优雅处理方法
- 解析c中stdout与stderr容易忽视的一些细节
- Windows系统下使用Sublime搭建nodejs环境
- nodejs实现获取某宝商品分类
- nodejs简单实现中英文翻译
- Node.js插件的正确编写方式
- 使用upstart把nodejs应用封装为系统服务实例
- NodeJS Web应用监听sock文件实例
- Nodejs学习笔记之测试驱动