单机多核,先用起来再说
很多 Node.js 项目第一次意识到进程管理,是在压测阶段。

明明服务器配了 8 核,但 Node.js 进程的 CPU 占用就是上不去,一查,只有其中一个核在忙。Node.js 的主线程是单线程的,这个问题不会因为内存变大而消失。要想利用多核,要么把服务拆成多实例,要么在同一台机器上拉起多个 Node.js 进程,让它们共享对外端口。
这时候,Node.js Cluster 模块和 PM2 会同时出现在搜索结果里。两个看起来都能做多进程,但选哪个,很多团队其实没想清楚。
先搞清楚 Cluster 模块到底做了什么
Cluster 模块是 Node.js 内置的,它让你可以创建多个工作进程(worker),由主进程(master)统一管理。worker 之间通过 IPC 通信,master 负责对外接收连接,再分发给空闲的 worker。严格说,它的核心能力不是实现负载均衡,而是让多个进程能够共享端口。
一个最基础的示例是这样的:
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const cpus = os.cpus().length;
for (let i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} 退出`);
});
} else {
require('./app.js');
}
这段代码在 master 里按 CPU 核数 fork 出 worker,每个 worker 都加载同一个 app.js。因为 worker 之间共享服务器句柄,所以多个 worker 可以同时监听同一个端口,由内核或主进程实现连接分发。在大多数平台上,Node.js 默认使用 round-robin 调度,对 HTTP 短连接的表现比较稳定。
但工程上会立刻遇到几个问题:如果 worker 崩溃退出,master 要不要拉一个新的?拉起来后旧请求如何处理?日志分散在这么多进程中,怎么统一查看?worker 之间的内存状态并不共享,某些依赖进程内缓存的服务,可能会拿到不一致的数据。Cluster 模块只是一个底层工具,它把问题从“怎么让 Node.js 多进程跑起来”推进到了“怎么把这些进程管好”。
另外,不要把 Cluster 模块和 worker_threads 搞混。Cluster 是进程级的多实例,每个 worker 都有独立的 V8 堆和事件循环;worker_threads 则是同一个进程内的多线程,共享内存,更适合 CPU 密集型任务。两者解决的问题并不一样。
- 误区一:Cluster 模块会自动保证 worker 随时可用?不会,它只提供 fork 和事件通知,重启策略要自己写。
- 误区二:所有 worker 都是无状态的?实际上任何第三方连接都各自独立,需要自己处理状态同步。
- 误区三:进程数必须等于 CPU 核数?这只是默认参考值,还得看内存和文件句柄。
PM2 的价值不是“多进程”,是“进程管理”
PM2 在很多项目里被当成“启动器”或“守护进程”,其实它最重要的能力,是把进程生命周期管理做成了标准化流程。PM2 的 cluster 模式在底层同样依赖 Node.js 的 cluster 机制,但它在这个基础上补齐了开发者在生产环境真正需要的东西:worker 崩溃自动重启、日志聚合、默认监控、开机自启、优雅停机和零停机重启。
用一个 ecosystem.config.js 文件就能把服务跑起来:
module.exports = {
apps: [{
name: 'api-service',
script: './src/server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
},
max_memory_restart: '1G'
}]
};
当你想更新代码时,执行 pm2 reload ecosystem.config.js,它会先让新 worker 起来,再慢慢放掉旧 worker,请求不会瞬间全部断裂。这里要区分 restart 和 reload。pm2 restart 会先停掉当前进程再启动一个新的,单个 worker 时会出现几十到几百毫秒的空窗;pm2 reload 则是滚动重启,逐个替换 worker,用户体验更平滑。
不是所有人都需要 PM2。如果你的服务已经跑在 Kubernetes 或者 Docker Swarm 上,存活检查和重启策略可以由平台负责,再套一个 PM2 反而多了一层状态和复杂度。PM2 更适合裸机、虚拟机和少量容器的部署场景。
选型不是看功能,而是看谁在管
我在不同团队里看到过两种极端:一种是所有服务都扔给 PM2,完全不区分业务特性;另一种是业务完全围绕 Node.js Cluster 模块自己封装,结果 master 进程里攒了不少定制化代码,最后还是在功能上逐渐追赶 PM2。
两种做法都有合理前提。自己封装 cluster,意味着你愿意承担进程调度的控制权。比如某些长连接服务,需要把特定类型的请求绑定到固定 worker,或者多个 worker 承担不同角色,而不是单纯的 HTTP 流量分发。这种情况下,PM2 的 cluster 模式不够灵活,因为它是按“任意请求分配到任意 worker”的统一模型设计的。
反过来,绝大多数 Web API、中间件、消息消费类服务,并不需要这种自定义。它们需要的是稳定性、可观察性和低运维成本,这是 PM2 擅长的地方。它牺牲了一部分底层控制力,但换来了更完整的管理闭环。
| 对比维度 | Cluster 模块 | PM2 cluster 模式 |
|---|---|---|
| 底层机制 | Node.js 内置多进程 | 底层调用 cluster,封装管理能力 |
| 多核利用 | 支持 | 支持 |
| 自动重启 | 需自己实现 | 内置,可配异常重启 |
| 日志处理 | 按进程输出,需自行聚合 | 统一采集、按时间切分 |
| 部署复杂度 | 代码控制,复杂度高 | 配置文件,上手快 |
| 适用场景 | 深度定制进程模型 | 标准 Web 服务快速上生产 |
在做决定前,可以问自己四个问题:
- 服务是否要求自定义调度策略?如果只是按端口分发,PM2 足够。
- 你们是否已有监控和日志体系?如果没有,PM2 能补上最基础的一块。
- 部署环境是否能容忍多一层进程守护?容器平台可能会和 PM2 冲突。
- 团队更愿意维护 Node.js 代码,还是更愿意维护配置文件?
生产环境里真正容易踩的坑
只看到多进程,忽略了状态同步
做了一个 cluster 服务后,登录状态一直随机失效,这种问题很典型。Node.js 的进程之间不共享内存,如果每个 worker 都维护自己的 session,同一个用户请求分配到不同 worker 时,就会表现得像被登出。解决方案是把 session、缓存等状态放到 Redis 或数据库,让所有 worker 访问同一份数据。这和 PM2 与否无关,只要用了多进程,都要面对。
PM2 不是跨机器负载均衡
PM2 的 cluster 模式只在当前主机内生效。如果你有多台机器,前面没有统一的流量入口,光靠 PM2 无法实现跨机调度。很多团队会误以为加了 instances: 'max' 就是分布式了,其实它只是在单片服务器上尽可能多地跑进程。
master 进程本身也是单点
Cluster 模块的 master 进程如果挂掉,所有 worker 都会失去容器。如果你的“守护方案”只是用 cluster.fork 拉起 worker,那就需要问自己一句:谁来守护 master?PM2 之所以被大量使用,正是因为它作为独立守护进程来监管整个集群,但这也意味着 PM2 进程本身如果退出,服务同样会停。所以生产环境中,常需要把它交给 systemd 或 Docker restart policy 来兜底。
worker 数量不是越多越好
每个 Node.js 进程都有自己的堆空间,假设每个 worker 占 150MB 内存,一个 2GB 的实例最多撑十几个进程。而且进程数超过核数后,上下文切换开销会上升,吞吐量不升反降。instances 的合理值需要结合 CPU、内存、以及上游连接池大小来定。达到极限时,先尝试减少进程数,比盲目加进程更有效。
给生产环境一个务实的路径
如果你正在从零搭建一个普通的 Node.js 服务,我的建议是:默认选择 PM2,优先让它把自动重启和日志聚合落地,再把业务做深。如果你维护的是一个组件型服务,需要在 worker 里做不同的业务分区,再考虑单独使用 Cluster 模块自制调度。
一个实际项目中,我倾向于这样分配:裸机或虚拟机上的多进程,交给 PM2;容器环境里单个容器的进程守护,交给平台本身;只有需要自定义 worker 角色的场景,保留 cluster 代码作为底层能力。这个区分不是为了技术上的洁癖,而是为了降低团队同时维护两套进程治理机制的成本。
长期来看,Node.js 进程管理的重点会慢慢从“怎么启动多个进程”转向“怎么让进程在故障时更快恢复”。无论选 Cluster 模块还是 PM2,建议都补上健康检查、优雅退出和状态外置。这些细节不会立刻被监控系统看到,但它们决定了你在凌晨两点能不能睡个好觉。
原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/606/