Node.js多进程集群部署,别让单进程扛整机
Node.js多进程和Cluster集群部署,别让一个进程扛完整台机器
Node.js 的单进程模型在理解上非常直观,但在生产环境中踩坑的姿势也相当丰富。

想象一下:你在一台 8 核机器上运行一个 Express 服务,没有做任何特殊处理。真正干活的 JavaScript 引擎只有一个进程。CPU 一号核心已经累垮了,而旁边的几个核心却悠闲得像在围观。线上流量一旦上来,表象就很诡异:机器整体 CPU 看起来并不高,但接口的 p99 延迟已经开始剧烈抖动。
这篇文章专门讲解 Node.js 的多进程方案和 Cluster 集群部署。环境采用 Node.js 24 与 Express 5.2.1。我们先使用 Node 官方 cluster 模块搭建一个可运行的示例,然后讨论它的边界在哪里,以及为什么生产环境中很多团队最终选择使用 PM2、systemd、Docker 或 Kubernetes 来管理进程。
Cluster 解决的核心问题
Node 官方文档对 cluster 的描述非常直白:它能够创建一组子进程,并且这些子进程可以共享同一个服务器端口。
这里的关键词是——进程。
Cluster 不是线程池,也不是把一个请求拆给多个核心并行计算。它的工作方式更像这样:主进程负责启动 worker,多个 worker 都监听同一个端口,请求进入后,由主进程分发给其中一个 worker 去处理。
这样做直接带来两个好处:
- 多核 CPU 终于能被充分利用起来。
- 某个 worker 崩溃后,主进程可以立刻启动一个新的。
代价也很现实:进程之间内存不共享。你存放在内存里的登录态、计数器、缓存,每个 worker 都各自有一份。一旦上了多进程,就千万别再将进程内存当作全局真理。
一个最小可运行的 Cluster 服务
首先安装 Express:
mkdir node-cluster-demo
cd node-cluster-demo
npm init -y
npm install express
server.js:
const cluster = require('node:cluster')
const os = require('node:os')
const process = require('node:process')
const express = require('express')
const port = Number(process.env.PORT || 3000)
const workerCount = Number(process.env.WORKERS || os.a vailableParallelism())
if (cluster.isPrimary) {
console.log(`primary ${ process.pid} is running`)
console.log(`starting ${ workerCount} workers`)
for (let i = 0; i < workerCount; i ) {
cluster.fork()
}
cluster.on('exit', (worker, code, signal) => {
console.error(`worker ${ worker.process.pid} died`, {code, signal })
cluster.fork()
})
} else {
const app = express()
app.get('/health', (req, res) => {
res.json({ ok: true, pid: process.pid,})
})
app.get('/cpu', (req, res) => {
const startedAt = Date.now()
while (Date.now() - startedAt < 80) {
Math.sqrt(Math.random())
}
res.json({ ok: true, pid: process.pid,})
})
app.listen(port, () => {
console.log(`worker ${ process.pid} listening on http://localhost:${ port}`)
})
}
启动服务:
node server.js
多请求几次:
curl http://localhost:3000/health
curl http://localhost:3000/health
curl http://localhost:3000/health
你会看到返回的 pid 可能不同——说明请求被分配到了不同的 worker 进程上。
这里特意用了 os.a vailableParallelism(),而不是以前习惯的 os.cpus().length。Node 官方在 os.cpus() 文档里也曾提醒过,不应拿它来计算应用可用并行度,推荐直接使用 os.a vailableParallelism()。
生产代码不能只会 fork
上面的代码能运行,但离生产环境还有好几步。至少需要处理优雅退出:
function createApp() {
const app = express()
app.get('/health', (req, res) => {
res.json({ok: true, pid: process.pid })
})
return app
}
if (cluster.isPrimary) {
for (let i = 0; i < workerCount; i ) {
cluster.fork()
}
cluster.on('exit', (worker, code, signal) => {
if (worker.exitedAfterDisconnect) { return }
console.error(`worker ${ worker.process.pid} crashed`, {code, signal })
cluster.fork()
})
} else {
const app = createApp()
const server = app.listen(port)
process.on('SIGTERM', () => {
server.close(() => {
process.exit(0)
})
setTimeout(() => {
process.exit(1)
}, 10_000).unref()
})
}
server.close() 会停止接收新连接,等已有的连接处理完成后再退出。外面再加一个 10 秒的兜底,是为了防止某些长连接或异常请求导致进程永远退不掉。
主进程里也别无脑重启所有退出的 worker。worker.exitedAfterDisconnect 这个属性可以帮助你区分“主动要求它退出”和“它意外崩溃”。这在滚动重启时尤其有用。
状态别放在进程内存里
这是 Cluster 最容易踩坑的地方。
假设你写了一个简单计数器:
let counter = 0
app.post('/count', (req, res) => {
counter = 1
res.json({counter, pid: process.pid })
})
单进程时它看起来没问题。多进程后,每个 worker 都有自己的 counter。请求打到 worker A,counter 是 10;下一个请求打到 worker B,counter 可能是 3。你以为自己写了全局计数器,实际上写了 N 个局部计数器。
登录态也一样。不要把 session 存在进程内存里,然后指望 Cluster 替你同步。应该放到 Redis、数据库,或者使用无状态 token。进程内缓存也要接受一个现实:每个 worker 都会各自缓存一份,命中率和内存占用都会受到影响。
曾经见过一个后台系统,上了 Cluster 之后验证码偶发校验失败。原因很朴实:验证码存在内存 Map 里,生成请求落到 worker 1,校验请求落到 worker 3,自然找不到了。
负载均衡不是魔法
Node cluster 支持两种分发方式。官方文档提到,除 Windows 外,默认是 round-robin,由主进程接收连接再分发给 worker。另一种方式是主进程创建监听 socket 后交给 worker,由 worker 自己 accept。
日常业务一般不用手动修改 cluster.schedulingPolicy。真正需要关心的是:worker 之间的负载可能仍然不均匀。
原因有很多:
- 请求耗时不同,慢请求会长期占用某个 worker
- 长连接(比如 WebSocket)会让连接长时间停留在某个 worker 上
- CPU 密集逻辑会阻塞单个 worker 的 event loop
所以 Cluster 并不是性能银弹。它只是让你把请求分散到多个进程。如果某个接口本身写得很重,多进程能缓解,但不能从根本上解决问题。
多进程下日志和监控要带 pid
上了 Cluster 之后,日志里必须带上 pid 或 worker id:
logger.info('request finished', {
pid: process.pid,
workerId: cluster.worker?.id,
method: req.method,
path: req.originalUrl,
status: res.statusCode,
})
否则你看到一段错误日志,很难判断是不是某一个 worker 持续出问题。比如 worker 4 内存一直上涨,最后反复重启。总览日志里只看到“进程重启了”,却看不到是哪一个,定位起来非常困难。
监控也一样。除了进程整体指标,我建议给每个 worker 都打上:
- RSS、heapUsed
- event loop delay
- 请求数和错误数
- 重启次数
多进程系统最怕平均值。平均 CPU 不高,不代表每个 worker 都健康;平均内存正常,也可能其中一个 worker 正在泄漏。
Cluster 和 PM2、容器怎么取舍
如果只是想理解原理,Node 内置 cluster 完全够用。
如果是生产部署,我的建议是按环境选择:
- 传统服务器:可以用 PM2 的 cluster mode 或 systemd 管理多个进程。PM2 省心,日志、重启、进程列表都有现成命令。systemd 更贴近系统层,适合不想引入额外 Node 进程管理工具的团队。
- Docker / Kubernetes 环境:我更倾向于一个容器只跑一个 Node 进程,然后通过多个副本来扩容。进程重启、健康检查、滚动发布、日志采集都交给平台。也可以在容器内再开 Cluster,但这样会让资源限制、优雅退出、监控粒度变得复杂。不是不能做,只是要有明确的理由。
- 本地开发和小型内网服务:直接用 cluster 也够。但别把它当成完整的进程平台——它不负责日志采集、发布编排、健康检查、限流熔断,也不会替你处理共享状态。
什么场景不适合只靠 Cluster
下面几种情况,纯 Cluster 往往搞不定:
- CPU 重任务:Cluster 能把请求分到多个进程,但单个请求里的 CPU 计算还是会堵住对应 worker。重计算更适合 Worker Threads、任务队列,或者拆成独立服务。
- 大量 WebSocket 长连接:连接会固定在某个 worker 上,负载均衡和会话管理需要额外设计。多实例部署时还要考虑消息广播和房间状态。
- 强依赖本地内存状态的应用:比如用本地 Map 存 session、验证码、临时任务状态。先把状态外置,再谈多进程。
- 超短任务但日志极重的服务:进程多了,日志竞争和 IO 压力也会上来。这时要先控制日志量。
收尾
Cluster 的价值很朴素:让 Node 服务用上多核,并且给 worker 崩溃后的恢复留一个入口。
但它也会逼你面对几个工程事实:
- 进程内存不是共享状态
- 日志和监控必须能区分 worker
- 优雅退出要自己处理
- 多进程只能缓解单进程瓶颈,不能修好糟糕的同步代码
我的建议是:先用 cluster 把多进程模型跑明白,再根据部署环境决定交给谁管理。传统机器上用 PM2 或 systemd,容器环境交给 Kubernetes 或编排平台。无论选择哪条路,都别让一个 Node 进程孤零零扛完整台机器。
参考来源
- Node.js cluster 官方文档:cluster 工作模型、
cluster.isPrimary、cluster.fork()、exit事件、调度策略,采集于 2026-06-29 - Node.js os 官方文档:
os.a vailableParallelism()与os.cpus()的使用建议,采集于 2026-06-29 - Node.js process 官方文档:
SIGTERM、进程事件与退出处理,采集于 2026-06-29 - npm 元数据:
express@5.2.1,采集于 2026-06-29
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
内网RPA离线部署从依赖打包到7×24无人值守踩坑与避坑方案
这三年,内网RPA项目接了不下二十个。每次开局都像闯关——断网、缺依赖、多机同步、定时执行、批量分发、源码保护、AI离线化,八个坑一个比一个深。今天把这些实战经验整理出来,希望能帮正在内网搞自动化的兄弟们少踩点雷。 一、内网无网络环境怎么部署RPA流程:先搞清楚什么叫“真离线” 很多工具宣传“支持本
水利工程师用WorkBuddy写洪水报告效率提升3倍
WorkBuddy开发者分享季 水利工程师AI提效实战:用WorkBuddy撰写洪水影响评价报告,效率提升3倍 WorkBuddy 效率 人工智能 开发工具 一、我是谁,为什么需要AI 先介绍一下自己——我是一名水利工程师,在湖南长沙的一家小型水利设计公司任职。当前行业环境不太
日志服务数据加工规则洞察仪表盘使用指南
数据加工诊断仪表盘 想实时掌握日志服务加工功能的运行状态?直接从加工列表页点击那个“规则洞察”按钮,仪表盘就会立刻呈现出来。入口就在那儿,不绕弯子。 跳转后,你可以按作业名称、实例ID或源LogStore来筛选任务状态。比如下边这张图,展示的是当前实例ID(90c9d47714dbb807d47c1
基于RFID的固定资产管理系统技术架构与工程实践
固定资产管理难题是众多企事业单位的普遍困扰,资产数量动辄数千件,且广泛分布于不同部门、楼层乃至园区。传统人工盘点方式在工程维度上始终面临三大关键瓶颈:采集效率低下、数据闭环中断、状态同步滞后。使用条码枪逐一扫描标签,识别距离通常不超过30厘米,操作人员需逐个寻找并扫描,盘点效率完全受限于人力。面对5
WorkBuddy实战用AI搭建A股智能盯盘助手省心高效
炒股的朋友们想必都深有体会——每天重复盯盘、查行情、分析板块轮动,这一整套流程下来耗费大量精力。手动翻查数据不仅身心俱疲,还很容易错过关键买卖节点。今天我们就来聊聊如何打造一款趁手的盯盘工具,借助AI替你分担这些重复性工作。 背景:盯盘的核心痛点 股民都有同感——每天不只要查询单只股票的实时行情,还
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-07-02 12:28
2026-07-02 12:27
2026-07-02 12:27
2026-07-02 12:27
2026-07-02 12:27
2026-07-02 12:27
2026-07-02 12:26
2026-07-02 12:26
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

