Node.js child_process 核心 API 详解

一、fork:专用 Node.js 进程创建

核心功能

创建一个新的 Node.js 子进程,自带进程间通信(IPC)通道

ini 复制代码
const { fork } = require('child_process');
const child = fork(path, [args], [options]);

参数解析

参数 类型 说明
path string 要运行的 JavaScript 文件路径
args Array 字符串参数列表
options Object 配置选项

关键配置项

arduino 复制代码
{
  execPath: '/usr/bin/node', // 指定 Node.js 解释器
  execArgv: ['--inspect'],   // 传递给 Node.js 的选项
  silent: true,              // 分离 stdio 不与父进程共享
  stdio: ['ipc', 'pipe', 'pipe'] // IPC 通道配置
}

通信机制

父进程 -> 子进程

go 复制代码
child.send({ type: 'COMMAND', data: payload });

// 子进程接收:
process.on('message', (msg) => {
  if (msg.type === 'COMMAND') {
    // 处理命令
  }
});

子进程 -> 父进程

javascript 复制代码
process.send({ status: 'READY', pid: process.pid });

// 父进程接收:
child.on('message', (msg) => {
  console.log(`Received from child: ${msg.status}`);
});

经典用例

javascript 复制代码
// 父进程
const apiWorker = fork('./api-service.js', ['production'], {
  execPath: '/usr/local/bin/node',
  silent: true
});

apiWorker.on('message', (msg) => {
  if (msg === 'SERVER_READY') {
    console.log('API service started');
    startWebServer();
  }
});

apiWorker.stdout.on('data', (data) => {
  console.log(`[API] ${data}`);
});
arduino 复制代码
// api-service.js(子进程)
const express = require('express');
const app = express();

// ...配置路由...

const server = app.listen(3000, () => {
  console.log('API service running on port 3000');
  
  // 通知父进程
  if (process.send) {
    process.send('SERVER_READY');
  }
});

二、exec:Shell 命令执行专家

核心功能

执行 shell 命令 并捕获输出结果

bash 复制代码
const { exec } = require('child_process');
exec(command, [options], [callback]);

参数解析

参数 类型 说明
command string 要执行的 shell 命令
options Object 进程执行选项
callback Function 带(error, stdout, stderr)

安全实践

javascript 复制代码
// 危险!存在命令注入风险
exec(`ls ${userInput}`); 

// 安全方案(使用 execFile)
execFile('ls', [sanitize(userInput)], (err, stdout) => {
  // 处理结果
});

配置选项详解

arduino 复制代码
{
  cwd: '/project',       // 工作目录
  env: { NODE_ENV: 'prod' }, // 环境变量
  timeout: 5000,         // 超时时间(ms)
  maxBuffer: 1024 * 1024, // 输出缓冲区大小(1MB)
  encoding: 'utf8',       // 输出编码
  shell: '/bin/bash'      // 指定 shell
}

完整示例

lua 复制代码
const { exec } = require('child_process');

exec('git pull origin master', {
  cwd: '/var/www/project',
  timeout: 10000
}, (error, stdout, stderr) => {
  if (error) {
    console.error(`部署失败: ${error.message}`);
    return;
  }
  
  console.log('Git 更新:');
  console.log(stdout);
  
  if (stderr) {
    console.warn('警告信息:');
    console.warn(stderr);
  }
  
  restartServices();
});

三、fork vs exec 比较

特性 fork exec
进程类型 新的 Node.js 实例 任意系统进程
通信机制 支持 IPC 通信 无内置通信机制
性能开销 较高 (需加载新 Node 实例) 较低
输出处理 通过 stdio 流处理 缓冲区返回
适用场景 Node 服务集群 执行系统命令/工具
执行控制 细粒度进程控制 命令级别控制
资源回收 需手动管理 自动回收

四、高级应用模式

1. 进程池管理

ini 复制代码
const { fork } = require('child_process');
const os = require('os');

class ProcessPool {
  constructor(scriptPath, size = os.cpus().length) {
    this.pool = [];
    this.taskQueue = [];
    
    // 初始化工作进程
    for (let i = 0; i < size; i++) {
      const worker = fork(scriptPath);
      worker.on('message', (result) => {
        this._returnWorker(worker, result);
      });
      this.pool.push(worker);
    }
  }
  
  execute(taskData) {
    return new Promise((resolve) => {
      if (this.pool.length > 0) {
        const worker = this.pool.pop();
        worker.send(taskData);
        worker.resolve = resolve;
      } else {
        this.taskQueue.push({ taskData, resolve });
      }
    });
  }
  
  _returnWorker(worker, result) {
    worker.resolve(result);
    
    if (this.taskQueue.length > 0) {
      const { taskData, resolve } = this.taskQueue.shift();
      worker.resolve = resolve;
      worker.send(taskData);
    } else {
      this.pool.push(worker);
    }
  }
}

// 使用示例
const pool = new ProcessPool('./task-processor.js', 4);
pool.execute({ data: 'process-this' })
  .then(result => console.log(result));

2. 超时控制增强

javascript 复制代码
function executeWithTimeout(command, timeout = 3000) {
  return new Promise((resolve, reject) => {
    const child = exec(command);
    let timedOut = false;
    
    const timer = setTimeout(() => {
      timedOut = true;
      child.kill('SIGTERM');
      reject(new Error(`Command timeout after ${timeout}ms`));
    }, timeout);
    
    child.on('exit', (code) => {
      clearTimeout(timer);
      if (!timedOut) {
        code === 0 
          ? resolve() 
          : reject(new Error(`Exited with code ${code}`));
      }
    });
  });
}

// 使用示例
executeWithTimeout('npm run build', 10000)
  .catch(err => console.error('构建失败:', err));

五、错误处理最佳实践

1. 错误分级处理

javascript 复制代码
child.on('error', (err) => {
  // 进程创建失败(如权限问题)
  console.error('FATAL:', err);
});

child.on('exit', (code, signal) => {
  if (code !== 0) {
    // 异常退出处理
    console.error(`Process crashed with code ${code}`);
  }
});

// exec 特例错误代码
exec('invalid-command', (err, stdout, stderr) => {
  if (err) {
    console.error('执行错误:');
    console.error(`- 代码: ${err.code}`);
    console.error(`- 信号: ${err.signal}`);
    console.error(`- 堆栈: ${err.stack}`);
  }
});

2. 跨进程错误传递

javascript 复制代码
// 子进程 (child.js)
try {
  // ...危险操作...
} catch (err) {
  process.send({
    type: 'ERROR',
    message: err.message,
    stack: err.stack
  });
  process.exit(1);
}

// 父进程
child.on('message', (msg) => {
  if (msg.type === 'ERROR') {
    console.error(`子进程错误: ${msg.message}`);
    console.error(msg.stack);
  }
});

六、性能优化技巧

  1. 共享内存转移 (V8.5+)
ini 复制代码
const largeBuffer = Buffer.alloc(1024 * 1024); // 1MB

// 传统方式:复制数据
child.send(largeBuffer);

// 高性能方式:共享内存
child.send(largeBuffer, [largeBuffer.buffer]);
  1. 连接复用
javascript 复制代码
// 建立长期存活的进程连接
const persistentChild = fork('daemon.js');

// 使用对象池复用连接
function request(data) {
  return new Promise(resolve => {
    persistentChild.send(data, resolve);
  });
}
  1. 流式数据处理
c 复制代码
// 子进程
process.stdin.pipe(transformStream).pipe(process.stdout);

// 父进程
const { spawn } = require('child_process');
const child = spawn('processor');

fs.createReadStream('input.txt')
  .pipe(child.stdin);

child.stdout
  .pipe(fs.createWriteStream('output.txt'));

七、安全加固指南

  1. 沙盒隔离
javascript 复制代码
const { fork } = require('child_process');
const vm = require('vm');

const sandbox = {
  console: console,
  require: name => {
    if (name !== 'safe-module') {
      throw new Error('Module not allowed');
    }
    return require(name);
  }
};

const script = new vm.Script('require("safe-module").process()');
const context = vm.createContext(sandbox);

fork('handler.js', {
  execArgv: ['-r', 'sandbox-module'],
  env: { ...process.env, SANDBOX_CONTEXT: JSON.stringify(context) }
});
  1. 权限限制
php 复制代码
const { exec } = require('child_process');

exec('sensitive-command', {
  // Linux 用户隔离
  uid: 1001,
  gid: 1001,
  
  // Windows 权限
  windowsVerbatimArguments: true
});

总结

方法 最佳适用场景 关键注意事项
fork Node.js 微服务、CPU 密集型任务、进程间通信 注意内存消耗,使用连接池管理
exec Shell 命令执行、外部工具调用、简单操作 严格限制 maxBuffer,防范命令注入攻击

实际选择原则

  1. 在 Node.js 进程间需要复杂通信时使用 fork

  2. 在调用系统命令或外部工具时使用 exec

  3. 在需要流式处理大量数据时考虑 spawn

  4. 在需要安全执行命令和解析参数时使用 execFile

通过合理使用这些 API,可以构建高性能、可扩展的 Node.js 应用,同时确保系统稳定和安全。

相关推荐
我是伪码农6 小时前
Vue 智慧商城项目
前端·javascript·vue.js
不认输的西瓜6 小时前
fetch-event-source源码解读
前端·javascript
用户39051332192886 小时前
前端性能杀手竟然不是JS?图片优化才是绝大多数人忽略的"降本增效"方案
前端
朱昆鹏7 小时前
开源 Claude Code + Codex + 面板 的未来vibecoding平台
前端·后端·github
lyrieek7 小时前
pgadmin的导出图实现,还在搞先美容后拍照再恢复?
前端
永远是我的最爱7 小时前
基于.NET的小小便利店前台收银系统
前端·sqlserver·.net·visual studio
从文处安7 小时前
「九九八十一难」第一难:前端数据mock指南(TS + VUE)
前端
Zhencode7 小时前
Vue3 响应式依赖收集与更新之effect
前端·vue.js
x-cmd7 小时前
[x-cmd] jsoup 1.22.1 版本发布,引入 re2j 引擎,让 HTML 解析更安全高效
前端·安全·html·x-cmd·jsoup
天下代码客8 小时前
使用electronc框架调用dll动态链接库流程和避坑
前端·javascript·vue.js·electron·node.js