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 应用,同时确保系统稳定和安全。

相关推荐
程序媛李李李李李蕾3 分钟前
你不能直接用现成的吗?整个前端做笔记管理工具真是折腾人
javascript·vue.js·后端
passer9819 分钟前
列表项切换时同步到可视区域
前端
FogLetter11 分钟前
移动端适配的终极奥义:从lib-flexible到postcss-pxtorem的全方位指南
前端·postcss
易元12 分钟前
设计模式-访问者模式
前端·后端·设计模式
兵临天下api12 分钟前
Elasticsearch 查询性能优化:从 3 秒到 300ms 的 6 个核心参数调优指南
前端
整点可乐14 分钟前
cesium实现鹰眼图
javascript·cesium
艾小码15 分钟前
Web存储指南:彻底掌握localStorage与sessionStorage
javascript
子林super20 分钟前
y1新建cluster集群redis
前端
liangdabiao23 分钟前
一篇文章尽快介绍入门级智能体Agent是什么回事, Starter AI Agents 项目 来自 awesome-llm-apps
前端·后端
BUG创建者27 分钟前
openlayer根据不同的状态显示不同的图层颜色
android·java·javascript