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

相关推荐
小小爱大王26 分钟前
AI 编码效率提升 10 倍的秘密:Prompt 工程 + 工具链集成实战
java·javascript·人工智能
半生过往27 分钟前
2025 前端动效实战指南:Vue Bits & React Bits 深度拆解(功能 / 复用 / 高频问题处理)
前端·vue.js·react.js
程序员包打听29 分钟前
Vitest 4.0 重磅发布:Browser Mode 正式稳定,前端测试进入新纪元
前端
BumBle30 分钟前
UniApp 多页面编译优化:编译时间从10分钟到1分钟
前端
星链引擎34 分钟前
大语言模型的技术突破与稳定 API 生态的构建
前端
还是大剑师兰特34 分钟前
TypeScript 面试题及详细答案 100题 (71-80)-- 模块与命名空间
前端·javascript·typescript
BumBle34 分钟前
使用 SortableJS 实现vue3 + Element Plus 表格拖拽排序
前端·vue.js·element
玉宇夕落34 分钟前
HTML5 音乐敲击乐静态界面
前端
海在掘金6112735 分钟前
告别"拼写错误":TS如何让你的代码"字字精准"
前端
用户479492835691535 分钟前
什么是XSS攻击,怎么预防,一篇文章带你搞清楚
前端·javascript·安全