一、Node.js 进程概述
- 进程(Process):操作系统中正在运行的程序实例。每个 Node.js 应用启动后就是一个进程。
- Node.js 是单线程的,但可以通过子进程实现多进程并发,提升性能或处理 CPU 密集型任务。
二、进程相关的核心模块
1. process 模块
Node.js 全局对象,代表当前运行的进程。
常用属性和方法:
process.pid:当前进程 IDprocess.argv:启动参数数组process.env:环境变量对象process.exit([code]):退出进程process.on('exit', cb):监听进程退出事件process.on('uncaughtException', cb):监听未捕获异常
示例:
console.log('进程ID:', process.pid);
console.log('启动参数:', process.argv);
console.log('环境变量:', process.env.NODE_ENV);
2. child_process 模块
用于创建和管理子进程。常用方法:
exec:执行命令,适合简单命令行任务spawn:启动新进程,流式处理标准输入/输出fork:专门用于 Node.js 子模块,适合进程间通信
三、创建子进程的三种方式
1. child_process.exec
执行 shell 命令,返回完整结果。
const { exec } = require('child_process');
exec('ls -l', (err, stdout, stderr) => {
if (err) {
console.error('执行错误:', err);
return;
}
console.log('输出:', stdout);
});
适合场景:执行简单命令,如文件操作、git 命令等。
2. child_process.spawn
启动新进程,适合处理大量数据流。
const { spawn } = require('child_process');
const child = spawn('node', ['-v']);
child.stdout.on('data', (data) => {
console.log(`子进程输出: ${data}`);
});
child.stderr.on('data', (data) => {
console.error(`错误输出: ${data}`);
});
child.on('close', (code) => {
console.log(`子进程退出码: ${code}`);
});
适合场景:持续处理输入输出,如音视频流、文件处理等。
3. child_process.fork
专门用于 Node.js 子模块,支持进程间通信(IPC)。
主进程:
const { fork } = require('child_process');
const child = fork('child.js');
child.on('message', (msg) => {
console.log('收到子进程消息:', msg);
});
child.send({ hello: 'world' });
子进程(child.js):
process.on('message', (msg) => {
console.log('收到主进程消息:', msg);
process.send({ received: true });
});
适合场景:多进程并发、任务分发、CPU 密集型计算。
四、进程间通信(IPC)
fork创建的子进程可通过send方法进行消息通信。- 适合主进程分配任务,子进程处理后返回结果。
五、应用场景举例
- CPU 密集型任务:如加密、图片处理,将任务分发到多个子进程,避免主线程阻塞。
- 任务分片:如爬虫、批量处理,将任务拆分给不同子进程。
- 服务集群:利用
cluster模块实现多进程负载均衡(每个进程监听同一个端口)。
六、cluster 模块(进阶)
cluster 用于创建多进程服务器,充分利用多核 CPU。
简单示例:
const cluster = require('cluster');
const http = require('http');
const os = require('os');
if (cluster.isMaster) {
const cpuCount = os.cpus().length;
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
} else {
http.createServer((req, res) => {
res.end('Hello from worker ' + process.pid);
}).listen(3000);
}
七、注意事项
- 子进程是独立的内存空间,变量不会共享。
- 进程间通信数据量大时建议采用流式处理。
- 子进程过多可能导致系统资源耗尽,要合理管理。
- 异常处理要完善,防止主进程或子进程崩溃。
八. 进程池的实现
问题: 如果每个任务都新建子进程,频繁创建/销毁会浪费资源。
解决方案: 建立“进程池”,维护一组固定数量的子进程,任务轮流分配。
简单进程池示例:
const { fork } = require('child_process');
const path = require('path');
class ProcessPool {
constructor(size, workerPath) {
this.pool = [];
this.tasks = [];
this.size = size;
this.workerPath = workerPath;
for (let i = 0; i < size; i++) {
const worker = fork(workerPath);
worker.busy = false;
worker.on('message', (msg) => {
worker.busy = false;
if (this.tasks.length) {
const task = this.tasks.shift();
worker.busy = true;
worker.send(task);
}
});
this.pool.push(worker);
}
}
runTask(data) {
const idleWorker = this.pool.find(w => !w.busy);
if (idleWorker) {
idleWorker.busy = true;
idleWorker.send(data);
} else {
this.tasks.push(data);
}
}
}
// worker.js
// process.on('message', (data) => {
// // 处理任务
// process.send({ done: true, data: data });
// });
// 用法
const pool = new ProcessPool(4, path.join(__dirname, 'worker.js'));
for (let i = 0; i < 10; i++) {
pool.runTask({ num: i });
}
九. cluster 的高级应用
负载均衡与自动重启
cluster 可以自动分发请求到不同 worker。
如果 worker 崩溃,可以自动重启:
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const cpuNum = os.cpus().length;
for (let i = 0; i < cpuNum; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died. Restarting...`);
cluster.fork();
});
} else {
require('./app'); // 你的服务器代码
}
十. 子进程异常处理与自动重启
- 监听
exit、error事件,及时处理异常。 - 可以设置最大重启次数,防止无限重启。
示例:
const { fork } = require('child_process');
function startWorker() {
const worker = fork('./worker.js');
worker.on('exit', (code) => {
if (code !== 0) {
console.log('Worker crashed, restarting...');
startWorker();
}
});
}
startWorker();
十一. 实际案例:多进程爬虫
主进程分配任务,子进程爬取数据并返回结果。
master.js:
const { fork } = require('child_process');
const urls = [/* 一堆要爬的网址 */];
const workerNum = 4;
let finished = 0;
for (let i = 0; i < workerNum; i++) {
const worker = fork('./crawler.js');
worker.on('message', (msg) => {
console.log('爬取结果:', msg);
finished++;
if (urls.length) {
worker.send(urls.pop());
} else if (finished >= workerNum) {
process.exit();
}
});
worker.send(urls.pop());
}
crawler.js:
const axios = require('axios');
process.on('message', async (url) => {
try {
const res = await axios.get(url);
process.send({ url, status: res.status });
} catch (e) {
process.send({ url, error: e.message });
}
});
十二. 性能与资源管理建议
- 进程数量:一般建议不超过 CPU 核心数。
- 内存管理:子进程内存独立,监控内存占用,及时回收。
- 任务分配:主进程负责调度,合理分配任务,避免某个子进程过载。
- 日志与监控:记录每个子进程的状态、异常、处理时间等,方便排查问题。
- 优雅退出:进程收到 SIGINT/SIGTERM 时,优雅关闭,处理未完成任务。
十三. 进程间复杂通信
1.1 消息传递机制(IPC)
fork 创建的子进程和父进程可以通过 process.send() 和 process.on('message', ...) 进行消息通信。
消息是 JSON 序列化的对象,适合传递结构化数据。
示例:主进程收集子进程结果
// master.js
const { fork } = require('child_process');
const child = fork('./worker.js');
child.on('message', (msg) => {
console.log('收到子进程消息:', msg);
});
child.send({ task: 'calculate', data: 123 });
// worker.js
process.on('message', (msg) => {
// 处理任务
let result = msg.data * 2;
process.send({ result });
});
1.2 数据流通信
对于大量数据(如文件、视频流),推荐用 spawn,通过 stdin/stdout 流进行通信。
示例:父进程传文件内容给子进程处理
const { spawn } = require('child_process');
const fs = require('fs');
const child = spawn('node', ['worker.js']);
fs.createReadStream('bigfile.txt').pipe(child.stdin);
child.stdout.on('data', data => {
console.log('处理结果:', data.toString());
});
1.3 共享内存(进阶)
Node.js 原生不支持进程间共享内存,但可以通过 worker_threads 使用 SharedArrayBuffer 实现线程级共享。
进程间如果需要共享状态,推荐用 Redis、数据库等外部存储。
十四. 子进程的资源限制与监控
2.1 设置资源限制
可通过启动参数控制子进程资源,如内存:
const child = fork('worker.js', [], { execArgv: ['--max-old-space-size=128'] }); // 限制最大内存128MB
2.2 监控进程健康
- 定期检测子进程是否存活(
process.kill(pid, 0)检查是否存在) - 通过心跳消息机制,父进程定时要求子进程回复,超时则重启
心跳机制示例:
// master.js
setInterval(() => {
child.send({ type: 'ping' });
// 如果长时间未收到 'pong',则重启
}, 1000);
// worker.js
process.on('message', (msg) => {
if (msg.type === 'ping') {
process.send({ type: 'pong' });
}
});
十五. 父子进程生命周期管理
- 父进程应监听子进程的
exit事件,及时重启或清理资源 - 子进程可通过
process.exit()主动退出 - 父进程退出时,建议优雅关闭所有子进程(可用
SIGTERM信号)
优雅退出示例:
process.on('SIGTERM', () => {
// 清理资源
process.exit(0);
});
十六. 多进程架构设计建议
- 任务分配策略:轮询分配、按负载分配、优先空闲进程
- 进程池管理:任务过多时排队,避免资源耗尽
- 异常恢复机制:自动重启、报警、日志记录
- 扩展性:进程池大小可动态调整,支持横向扩展
- 与主线程通信协议:设计好消息格式,避免消息丢失或混乱
十七. 进程与线程的区别及混合使用
- 进程:独立内存空间,适合分布式、并发任务、CPU密集型
- 线程(worker_threads):共享内存,适合高频小任务、共享状态
混合使用场景:
- 主进程调度任务,子进程负责大任务,子进程内部用 worker_threads 并行处理
- 例如:视频转码、图片批量处理
worker_threads 简单示例:
const { Worker } = require('worker_threads');
const worker = new Worker('./thread.js', { workerData: { num: 123 } });
worker.on('message', msg => console.log('线程结果:', msg));
十八. 常见问题与解决办法
- 子进程僵尸化:及时监听并回收
- IPC消息丢失:设计重试机制
- 内存泄漏:监控内存,定期重启进程
- 进程间同步:用外部存储(Redis、数据库)做协调
十九. 推荐工具
- PM2:自动进程管理、负载均衡、日志收集
- node-cluster:多进程 HTTP 服务
- Bull/Agenda:任务队列与分布式处理
创作不易,点赞关注,持续更新!!!
1492

被折叠的 条评论
为什么被折叠?



