主题
Node.js 进程与性能
Node.js 默认是单进程单线程模型。要充分利用多核 CPU、处理 CPU 密集型任务,需要理解 Cluster、Worker Threads 以及性能调优手段。
作者:yanshaodong
单线程模型的优势与瓶颈
- 优势:无锁竞争、无上下文切换开销、事件循环高效处理 I/O 密集型请求。
- 瓶颈:单个 CPU 核心只能跑一个事件循环;一旦执行重计算(如大 JSON 解析、加密、图像处理),会阻塞整个事件循环,所有请求都被卡住。
Cluster 多进程
通过 cluster 模块 fork 多个子进程(每个核一个),共享同一端口,由操作系统分发请求,实现多核并行。
javascript
const cluster = require('cluster')
const os = require('os')
const http = require('http')
if (cluster.isPrimary) {
// 主进程:按 CPU 核心数 fork 子进程
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork()
}
cluster.on('exit', (worker) => {
console.log(`进程 ${worker.process.pid} 退出,重启中`)
cluster.fork()
})
} else {
// 子进程:每个都启动一个 HTTP 服务
http.createServer((req, res) => {
res.end('handled by ' + process.pid)
}).listen(3000)
}- 主进程不处理业务,只负责调度与故障重启。
- 子进程之间内存隔离,共享状态需借助外部存储(Redis)或 IPC 通信。
Worker Threads
用于CPU 密集型任务,避免阻塞主线程的事件循环。与 Cluster 不同,Worker 是线程(共享内存可经 SharedArrayBuffer),而非独立进程。
javascript
const { Worker } = require('worker_threads')
// main.js
const worker = new Worker('./compute.js')
worker.on('message', (result) => {
console.log('计算结果:', result)
})
worker.postMessage({ num: 40 }) // 发送任务
// compute.js
const { parentPort } = require('worker_threads')
parentPort.on('message', ({ num }) => {
// 此处执行重计算,不会阻塞主线程
parentPort.postMessage(fibonacci(num))
})适用场景:加解密、压缩、图像/视频处理、复杂算法。
内存与 CPU 调优
内存限制
Node 默认堆内存上限约为 2GB(旧版 1.5GB),超出会 OOM。
bash
node --max-old-space-size=4096 app.js # 提升堆上限到 4GB若应用确实需要超大堆,优先排查内存泄漏,而非一味调大上限。
常见内存泄漏来源
- 全局变量 / 闭包持有大对象未释放。
- 未清理的定时器
setInterval。 - 监听器重复绑定(
EventEmitter泄漏)。 - 缓存无限增长(应使用 LRU 策略)。
性能诊断工具
bash
node --inspect app.js # 开启调试,Chrome DevTools 远程分析
node --prof app.js # 生成 V8 profiling 日志配合 clinic.js、0x 等工具可生成火焰图定位热点。
守护与进程管理
生产环境应使用进程管理器保证可用性:
| 工具 | 特点 |
|---|---|
| PM2 | 最流行,支持集群、日志、监控、零停机重启 |
| Docker + restart policy | 容器化部署,崩溃自动拉起 |
| systemd | Linux 原生服务管理 |
bash
pm2 start app.js -i max # 按 CPU 核数启动集群
pm2 monit # 实时监控
pm2 reload app.js # 零停机重启优雅退出
收到终止信号时,应停止接收新请求、处理完在途请求后再退出。
javascript
process.on('SIGTERM', () => {
server.close(() => {
console.log('已停止接收新连接')
process.exit(0)
})
// 超时强退兜底
setTimeout(() => process.exit(1), 10000).unref()
})作者:yanshaodong