Node子进程进阶
最近在做12306爬虫,刚好遇到了Node子进程的应用场景,不总结不快:
const { spawn, exec, execFile ,fork } = require('child_process');
Node的child_process
模块提供了衍生子进程的功能,通常子进程开启后,会与父进程建立 stdin
、stdout
和 stderr
的管道。如上代码所示,child_process
模块暴露了4个方法,其中spawn
为“根本大法”,其余的3个均在其基础上衍生而来,而每个方法都有同步和异步两个版本,本文主要以异步方法为主,因为结合Node单线程和12306爬虫这个web应用来说,谈论异步场景更为合适。
spawn(command[, args][, options])
#运行 ls -lh /usr,并捕获 stdout、stderr、以及退出码(取自Node官网)
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`子进程退出码:${code}`);
});
exec(command[, options][, callback])
此方法生成一个 shell 并在 shell 中执行 command
const { exec } = require('child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
if (error) {
console.error(`执行出错: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
});
execFile(file[, args][, options][, callback])
本方法与exec()
类似,只是不衍生 shell。 file
脚本文件会被直接衍生为一个新进程,这使得它比 child_process.exec()
更高效
const { execFile } = require('child_process');
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
if (error) {
throw error;
}
console.log(stdout);
});
可以使用util.promisify()
进行Promise化:
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
async function getVersion() {
const { stdout } = await execFile('node', ['--version']);
console.log(stdout);
}
getVersion();
fork(modulePath[, args][, options])
fork()
方法用于专门衍生新的 Node.js 进程,衍生的 Node.js 子进程与两者之间建立的 IPC 通信信道的异常是独立于父进程的。 每个进程都有自己的内存,使用自己的 V8 实例。 由于需要额外的资源分配,因此不建议衍生大量的 Node.js 进程。是spawn ()
方法的一种特殊情况。
总结
spawn()
, exec()
, execFile()
,fork()
都遵循 Node.js 惯用的异步编程模式。每个方法都返回 ChildProcess
实例。这些对象都实现了 EventEmitter
接口,允许父进程注册监听器函数,在子进程生命周期期间,当特定的事件(close
/disconnect
/error
/exit
/message
)发生时会调用这些函数。
其中,exec()
和 execFile()
可以额外指定 callback
函数,当子进程结束时会被调用。