何时用

Node 单进程只能用一个 CPU 核——多核机器跑 HTTP 服务时浪费。cluster 让你在多个进程之间共享同一个端口。

master 进程
  ├── worker 1 (跑 HTTP server)
  ├── worker 2 (跑 HTTP server)
  ├── worker 3
  └── worker 4
            ↓
所有 worker 共享一个端口 → 内核负载均衡

基础

import cluster from 'cluster';
import http from 'http';
import os from 'os';

if (cluster.isPrimary) {
    const numCPUs = os.cpus().length;
    console.log(`Master ${process.pid} starting ${numCPUs} workers`);

    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died, restarting`);
        cluster.fork();
    });
} else {
    // worker 进程
    http.createServer((req, res) => {
        res.end(`Hello from worker ${process.pid}\n`);
    }).listen(8080);
}

跑:

node app.js
# Master 12345 starting 8 workers
# 8 个 worker 共享 8080 端口

主从通信

// master 发给 worker
worker.send({ task: 'reload-config' });

// worker 收
process.on('message', msg => {
    if (msg.task === 'reload-config') reloadConfig();
});

// worker 发给 master
process.send({ status: 'ready' });

// master 收
cluster.on('message', (worker, msg) => {
    console.log(`worker ${worker.id} says:`, msg);
});

优雅重启(zero-downtime)

function reload() {
    const workers = Object.values(cluster.workers);
    workers.forEach((worker, i) => {
        setTimeout(() => {
            worker.disconnect();     // 不再接新连接
            worker.on('exit', () => {
                cluster.fork();       // 新起一个
            });
        }, i * 1000);                  // 错开重启
    });
}

process.on('SIGUSR2', reload);        // kill -SIGUSR2 <pid> 触发

cluster vs PM2

实战几乎都用 PM2 而不是自己写 cluster——它内置 cluster 模式 + 监控 + 日志:

pm2 start app.js -i max       # max = 自动按 CPU 核数
pm2 start app.js -i 4         # 固定 4 个
pm2 reload app                # 优雅重启

PM2 帮你:

  • 自动重启崩溃的进程
  • 滚动重启(一个一个换)
  • 监控仪表板
  • 日志聚合

生产推荐 PM2——自己写 cluster 主要用于学习 / 特殊场景。

cluster vs 反向代理

方案 A:Node cluster(同一进程组)
互联网 → Node cluster → workers

方案 B:反向代理(多进程独立)
互联网 → Nginx → Node:3001
                  ↓ Node:3002
                  ↓ Node:3003

方案 B 优点:

  • 进程隔离更彻底(一个挂不影响别的)
  • Node 升级 / 重启更灵活
  • 反向代理还能做静态文件 / 缓存 / 限流

多数生产用方案 B(PM2 + Nginx)—— cluster 适合不想要反向代理的简单场景。

共享 state 的麻烦

// ❌ 每个 worker 一份独立 counter,互不知道
let counter = 0;
http.createServer((req, res) => {
    counter++;
    res.end(`Count: ${counter}`);
}).listen(8080);

要"全局"状态用:

  • Redis(共享 counter / session)
  • 数据库
  • 共享文件(不推荐)

worker_threads vs cluster

worker_threads cluster
隔离 同进程,共享内存 独立进程
启动开销
内存 共享 V8 每个独立 V8
适合 CPU 密集计算 并发 HTTP 处理

  • 不是所有应用都该用 cluster——IO 密集(多数 Web)用 cluster 受益不大(单进程也能扛很多)
  • worker 之间不共享 cache / state——重新设计架构
  • cluster + SQLite 容易锁竞争——多进程数据库选 Postgres
  • 实战首选 PM2,自己写 cluster 容易出错

下一篇:crypto。