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

相关推荐
言萧凡_CookieBoty39 分钟前
AI 编程省 Token 实战:从 Spec、上下文工程到模型分层的降本策略
前端·ai编程
DFT计算杂谈1 小时前
wannier90 参数详解大全
java·前端·css·html·css3
铁皮饭盒2 小时前
第2课:5分钟!用 Trae AI 生成你的第一个后端服务(Bunjs + Elysia)
前端·后端·全栈
之歆3 小时前
DAY13_CSS3进阶完全指南 —— 背景、边框、文本、渐变、滤镜与 Web 字体(下)
前端·css·css3
剑神一笑3 小时前
CSS 阴影生成器:从单层到多层叠加的艺术
前端·css·css3
lljss20203 小时前
1. NameServer 域名服务器---NS
linux·服务器·前端
anOnion4 小时前
构建无障碍组件之Tooltip Pattern
前端·html·交互设计
陈随易4 小时前
为什么今天还会有新语言?MoonBit 想解决什么问题?
前端·后端·程序员
西洼工作室4 小时前
unipp+vue3+python h5+app极验验证码集成全流程解析
前端·uni-app·全栈·极验