您现在的位置是:网站首页 > child_process模块文章详情
child_process模块
陈川
【
Node.js
】
54534人已围观
6605字
child_process模块简介
Node.js的child_process模块允许创建子进程来执行系统命令或其他Node.js脚本。这个模块提供了多种方法来创建和管理子进程,包括exec、execFile、spawn和fork等。通过子进程可以充分利用多核CPU资源,执行耗时操作而不会阻塞主事件循环。
创建子进程的方法
exec方法
exec
方法用于执行简单的shell命令,它会缓冲输出并在回调函数中返回:
const { exec } = require('child_process');
exec('ls -lh', (error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error}`);
return;
}
console.log(`标准输出:\n${stdout}`);
if (stderr) {
console.error(`标准错误输出:\n${stderr}`);
}
});
exec适合执行快速完成的命令,因为它会将所有输出缓存在内存中。对于大量输出的命令,可能会消耗大量内存。
execFile方法
execFile
与exec类似,但不通过shell执行命令,直接运行可执行文件:
const { execFile } = require('child_process');
execFile('node', ['--version'], (error, stdout, stderr) => {
if (error) {
throw error;
}
console.log(stdout);
});
这种方法更安全,因为它不调用shell,减少了shell注入攻击的风险。
spawn方法
spawn
是更底层的API,适合处理大量数据或需要流式处理的场景:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`标准输出: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`标准错误输出: ${data}`);
});
ls.on('close', (code) => {
console.log(`子进程退出码: ${code}`);
});
spawn返回一个ChildProcess对象,可以通过事件监听器处理输出。这种方法不会缓冲所有输出,适合处理大量数据。
fork方法
fork
是spawn的特殊形式,专门用于创建新的Node.js进程:
const { fork } = require('child_process');
const child = fork('./child.js');
child.on('message', (msg) => {
console.log('来自子进程的消息:', msg);
});
child.send({ hello: 'world' });
fork创建的进程会自动建立IPC通道,父子进程可以通过send()和message事件通信。子进程文件child.js示例:
process.on('message', (msg) => {
console.log('来自父进程的消息:', msg);
process.send({ foo: 'bar' });
});
子进程选项配置
创建子进程时可以传递选项对象进行配置:
const { spawn } = require('child_process');
const child = spawn('prg', [], {
stdio: 'pipe', // 标准输入输出配置
cwd: '/usr', // 工作目录
env: { // 环境变量
NODE_ENV: 'development'
},
detached: true, // 使子进程独立于父进程
shell: true // 使用shell执行命令
});
常用选项包括:
cwd
:设置子进程的工作目录env
:环境变量键值对stdio
:配置子进程的标准输入输出detached
:是否让子进程独立运行uid
/gid
:设置进程的用户/组标识
进程间通信
使用标准输入输出
通过stdio选项可以配置进程间的通信管道:
const { spawn } = require('child_process');
const grep = spawn('grep', ['ssh'], {
stdio: ['pipe', 'pipe', process.stderr]
});
grep.stdin.write('hello\n');
grep.stdin.write('ssh connection\n');
grep.stdin.end();
grep.stdout.on('data', (data) => {
console.log(`匹配的行: ${data}`);
});
使用IPC通道
fork创建的进程可以通过IPC通道通信:
// parent.js
const { fork } = require('child_process');
const child = fork('./child.js');
child.on('message', (msg) => {
console.log('父进程收到:', msg);
});
setInterval(() => {
child.send({ counter: Math.random() });
}, 1000);
// child.js
process.on('message', (msg) => {
console.log('子进程收到:', msg);
process.send({ response: 'pong' });
});
错误处理和进程管理
错误处理
子进程可能因多种原因失败,需要妥善处理:
const { spawn } = require('child_process');
const child = spawn('cmd_that_might_fail');
child.on('error', (err) => {
console.error('启动子进程失败:', err);
});
child.stderr.on('data', (data) => {
console.error(`子进程错误: ${data}`);
});
child.on('exit', (code, signal) => {
if (code) console.log(`子进程退出,代码 ${code}`);
if (signal) console.log(`子进程被信号终止: ${signal}`);
});
进程管理
可以控制子进程的生命周期:
const { spawn } = require('child_process');
const child = spawn('long_running_process');
// 5秒后终止子进程
setTimeout(() => {
child.kill('SIGTERM');
}, 5000);
// 强制终止
setTimeout(() => {
child.kill('SIGKILL');
}, 7000);
实际应用场景
并行处理任务
利用多核CPU并行处理任务:
const { fork } = require('child_process');
const os = require('os');
const cpuCount = os.cpus().length;
const workers = [];
for (let i = 0; i < cpuCount; i++) {
workers.push(fork('./worker.js'));
}
// 分发任务
workers.forEach((worker, index) => {
worker.send({ task: `task_${index}` });
});
执行系统命令
安全地执行系统命令并处理结果:
const { exec } = require('child_process');
function safeExec(cmd, callback) {
exec(cmd, { timeout: 5000 }, (error, stdout, stderr) => {
if (error) return callback(error);
if (stderr) return callback(new Error(stderr));
callback(null, stdout);
});
}
safeExec('ls -l', (err, result) => {
if (err) return console.error(err);
console.log(result);
});
长时间运行的服务
监控并重启崩溃的子进程:
const { spawn } = require('child_process');
function startService() {
const service = spawn('node', ['service.js']);
service.on('exit', (code) => {
console.log(`服务退出,代码 ${code}`);
if (code !== 0) {
console.log('5秒后重启服务...');
setTimeout(startService, 5000);
}
});
return service;
}
startService();
性能和安全考虑
性能优化
处理大量数据时使用流:
const { spawn } = require('child_process');
const fs = require('fs');
const input = fs.createReadStream('large_input.txt');
const transform = spawn('tr', ['a-z', 'A-Z']);
const output = fs.createWriteStream('large_output.txt');
input.pipe(transform.stdin);
transform.stdout.pipe(output);
安全最佳实践
避免shell注入攻击:
// 不安全的做法
const userInput = '; rm -rf /';
exec(`ls ${userInput}`);
// 安全的做法
const { execFile } = require('child_process');
execFile('ls', [userInput], (err, stdout) => {
// 处理结果
});
限制资源使用:
const { spawn } = require('child_process');
const child = spawn('compute_intensive_task', [], {
stdio: 'ignore',
detached: true,
// 限制资源
resourceLimits: {
maxOldGenerationSizeMb: 512,
maxYoungGenerationSizeMb: 256,
cpuTime: 1000
}
});
高级用法
重定向输入输出
将子进程输出重定向到文件:
const { spawn } = require('child_process');
const fs = require('fs');
const out = fs.openSync('./out.log', 'a');
const err = fs.openSync('./err.log', 'a');
const child = spawn('prg', [], {
stdio: ['ignore', out, err],
detached: true
});
child.unref();
集群管理
结合cluster模块创建进程集群:
const cluster = require('cluster');
const { fork } = require('child_process');
if (cluster.isMaster) {
// 主进程
const cpuCount = require('os').cpus().length;
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.id} died`);
cluster.fork();
});
} else {
// 工作进程
require('./worker.js');
}
跨平台兼容性
处理不同平台的命令差异:
const { exec } = require('child_process');
const os = require('os');
const command = os.platform() === 'win32'
? 'dir'
: 'ls';
exec(command, (err, stdout) => {
if (err) throw err;
console.log(stdout);
});