Node.js的Cluster模块可以让单个Node.js应用创建多个子进程,这些子进程共享同一个服务器端口,由主进程负责调度请求分配给不同的工作进程,从而充分利用多核CPU的计算能力,提升应用的整体性能。Cluster模块内置了负载均衡机制,默认采用轮询的方式将新连接分配给空闲的工作进程,不需要开发者手动实现进程调度逻辑。

Cluster模块的核心原理
Cluster模块的工作模式分为主进程和工作进程两个角色。主进程负责启动、管理工作进程,本身不处理具体的业务请求;工作进程负责接收主进程分配的请求,执行具体的业务逻辑。当应用启动时,主进程会先创建指定数量的工作进程,这些工作进程会监听同一个服务器端口,当有新的网络请求到达时,主进程会根据负载均衡策略将请求句柄传递给对应的工作进程处理。
基础多进程应用实现
下面通过一个简单的HTTP服务示例,展示如何使用Cluster模块创建多进程应用。首先需要根据CPU核心数决定创建工作进程的数量,通常工作进程数量和CPU核心数保持一致,能最大化利用硬件资源。
const cluster = require('cluster');
const http = require('http');
const os = require('os');
// 判断当前进程是否为主进程
if (cluster.isMaster) {
const cpuNum = os.cpus().length;
console.log(`主进程 ${process.pid} 正在运行,将创建 ${cpuNum} 个工作进程`);
// 根据CPU核心数创建工作进程
for (let i = 0; i < cpuNum; i++) {
cluster.fork();
}
// 监听工作进程退出事件,自动重启退出的进程
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出,正在重启新进程`);
cluster.fork();
});
} else {
// 工作进程创建HTTP服务
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`请求由工作进程 ${process.pid} 处理`);
}).listen(3000);
console.log(`工作进程 ${process.pid} 已启动,监听3000端口`);
}
运行上述代码后,访问localhost:3000,会看到响应内容中的进程ID不断变化,说明请求被不同的工作进程处理了。如果手动杀死某个工作进程,主进程会自动创建新的工作进程补充,保证服务的高可用。
主进程与工作进程的通信
在实际开发中,主进程和工作进程可能需要交换数据,Cluster模块基于child_process模块的通信机制,支持进程间的消息传递。主进程可以通过worker.send()向指定工作进程发送消息,工作进程可以通过process.send()向主进程发送消息,双方通过监听message事件接收消息。
// 主进程代码(cluster.isMaster分支内)
// 向所有工作进程发送消息
for (const id in cluster.workers) {
cluster.workers[id].send({
type: 'update_config',
data: { maxRequest: 1000 }
});
}
// 接收工作进程发送的消息
cluster.on('message', (worker, message) => {
console.log(`收到工作进程 ${worker.process.pid} 的消息:`, message);
});
// 工作进程代码(cluster.isMaster分支外)
// 接收主进程发送的消息
process.on('message', (msg) => {
if (msg.type === 'update_config') {
console.log(`工作进程 ${process.pid} 收到配置更新:`, msg.data);
}
});
// 向主进程发送消息
process.send({
type: 'status',
data: { pid: process.pid, memoryUsage: process.memoryUsage() }
});
使用Cluster模块的注意事项
- 工作进程之间是相互独立的,一个工作进程崩溃不会影响其他工作进程,但是共享内存数据无法直接在工作进程之间同步,需要通过主进程中转或者外部存储实现数据共享。
- Cluster模块的负载均衡默认在Windows系统下和类Unix系统下的实现略有差异,类Unix系统下默认使用轮询策略,Windows下使用其他方式,不过对上层业务的影响很小。
- 不要在主进程中执行耗时的业务逻辑,主进程只负责进程管理,避免主进程阻塞导致无法及时调度工作进程或者处理工作进程的异常事件。
- 如果应用需要处理大量长时间连接的场景,比如WebSocket服务,需要注意Cluster模块的负载均衡是基于连接的,长时间连接会占用工作进程的资源,可能需要调整负载均衡策略或者限制单进程的连接数。
进程状态监控示例
可以通过定期收集工作进程的状态信息,实现对多进程应用的监控,以下是一个简单的监控示例:
if (cluster.isMaster) {
setInterval(() => {
const workers = cluster.workers;
for (const id in workers) {
const worker = workers[id];
// 向工作进程请求状态信息
worker.send({ type: 'get_status' });
}
}, 5000);
cluster.on('message', (worker, message) => {
if (message.type === 'status_report') {
console.log(`工作进程 ${worker.process.pid} 状态:`, {
uptime: message.data.uptime,
memory: message.data.memoryUsage.heapUsed / 1024 / 1024 + 'MB'
});
}
});
} else {
process.on('message', (msg) => {
if (msg.type === 'get_status') {
process.send({
type: 'status_report',
data: {
uptime: process.uptime(),
memoryUsage: process.memoryUsage()
}
});
}
});
}
通过上述方式,开发者可以快速搭建稳定、高性能的Node.js多进程应用,充分利用服务器的多核资源,提升应用的并发处理能力和容错能力。在实际生产环境中,还可以结合进程守护工具、日志收集工具进一步完善多进程应用的运维体系。