Node 如何利用多核?cluster、worker_threads 与 PM2 集群模式有什么区别?
先理解原理和示例,再练习完整口述与追问。
回答要点
Node 的 JS 主要运行在单个线程,但 I/O 由 libuv 线程池和系统异步接口处理。利用多核有两类:cluster 或 PM2 cluster 模式创建多个进程共享同一端口,适合 HTTP 服务横向扩展;worker_threads 在同一进程内开线程,适合 CPU 密集计算并可共享内存。进程间通过 IPC 消息通信,状态需外置到 Redis 等。
原理拆解
cluster 主进程 fork 出多个工作进程,默认(Windows 除外)由主进程接受连接并轮询分发给工作进程。每个工作进程有独立的 V8 堆和事件循环,一个崩溃不影响其他,主进程监听 exit 后重启。会话、WebSocket 连接、内存缓存不能在进程之间共享,需要 Redis 或粘性会话。
PM2 的 -i max 基于 cluster 实现,提供进程守护、零停机重载(pm2 reload)、日志与监控。worker_threads 适合图片处理、加密、大 JSON 解析等会阻塞事件循环的任务,可以传递 SharedArrayBuffer 或转移 ArrayBuffer。uncaughtException 之后进程状态可能已不一致,应记录日志并优雅退出,由守护进程重启,而不是继续服务。
示例与推演
import cluster from 'node:cluster'
import { availableParallelism } from 'node:os'
import http from 'node:http'
if (cluster.isPrimary) {
for (let i = 0; i < availableParallelism(); i++) cluster.fork()
cluster.on('exit', worker => { console.error(`worker ${worker.process.pid} 退出,重新创建`); cluster.fork() })
} else {
http.createServer((req, res) => res.end(`pid ${process.pid}`)).listen(3000)
}
易错点
不要说“Node 只能用单核”或“错误没有堆栈”;容器环境中已由编排系统按副本扩容时,再在容器内开 cluster 可能重复占用资源。libuv 线程池默认 4 个线程,大量 fs 或加密操作时可通过 UV_THREADPOOL_SIZE 调整。
参考回答可直接练习口述
Node 的 JS 执行在单线程上,但 I/O 由 libuv 线程池和系统异步接口完成,并不是只能用一个核。对 Web 服务,用 cluster 或 PM2 集群模式起多个进程共享端口,每个进程独立事件循环,崩溃了由主进程重启,状态放 Redis;对 CPU 密集计算,用 worker_threads 在进程内开线程,避免阻塞事件循环。发生 uncaughtException 时应记录后优雅退出、由守护进程拉起。