Cluster: масштабирование
cluster — встроенный модуль Node.js, позволяющий создать несколько дочерних процессов (workers) на основе одного мастер-процесса, каждый из которых слушает один и тот же порт, используя все CPU-ядра.
Зачем нужно
Node.js по умолчанию использует одно ядро CPU. На 8-ядерном сервере 7 ядер простаивают. Cluster создаёт столько воркеров, сколько ядер, и балансирует входящие соединения между ними через операционную систему (round-robin). Это увеличивает throughput пропорционально числу ядер. В production чаще используют pm2 -i max вместо ручного cluster.
Где используется
- Production-серверы без Docker/Kubernetes (VPS)
- Когда нужно максимальное использование CPU
- Под капотом PM2 cluster mode
- Замена Worker Threads для HTTP-нагрузки (I/O bound)
Основной контент
Базовый пример
const cluster = require('cluster');
const http = require('http');
const os = require('os');
const numCPUs = os.cpus.length;
if (cluster.isPrimary) {
// Мастер-процесс: форкает воркеры
console.log(`Primary ${process.pid} is running`);
console.log(`Forking ${numCPUs} workers`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork;
}
// Перезапускать воркер при сбое
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
cluster.fork;
});
cluster.on('online', (worker) => {
console.log(`Worker ${worker.process.pid} is online`);
});
} else {
// Воркеры: запускают HTTP-сервер на том же порту
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from worker ${process.pid}\n`);
}).listen(3000);
console.log(`Worker ${process.pid} started`);
}
С Express
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
const numWorkers = os.cpus.length;
console.log(`Starting ${numWorkers} workers`);
for (let i = 0; i < numWorkers; i++) {
cluster.fork;
}
cluster.on('exit', (worker, code, signal) => {
if (!worker.exitedAfterDisconnect) { // не перезапускать при graceful shutdown
console.log(`Restarting worker ${worker.process.pid}`);
cluster.fork;
}
});
// Graceful shutdown
process.on('SIGTERM', () => {
for (const id in cluster.workers) {
cluster.workers[id].disconnect();
}
});
} else {
// Обычный Express-сервер
const express = require('express');
const app = express;
app.use(require('./routes'));
app.listen(3000, () => console.log(`Worker ${process.pid} listening on 3000`));
}
Коммуникация между мастером и воркерами
if (cluster.isPrimary) {
const worker = cluster.fork;
// Мастер отправляет воркеру
worker.send({ type: 'config', data: { maxConnections: 100 } });
// Мастер получает от воркера
worker.on('message', (msg) => {
console.log(`From worker: ${JSON.stringify(msg)}`);
});
} else {
// Воркер получает от мастера
process.on('message', (msg) => {
if (msg.type === 'config') {
console.log('Config received:', msg.data);
}
});
// Воркер отправляет мастеру
process.send({ type: 'stats', requests: 42 });
}
Cluster vs Worker Threads vs PM2
cluster:
+ Несколько OS процессов (изоляция памяти)
+ Каждый воркер — отдельный V8 (crash-isolated)
- Нет разделяемой памяти
- Нужен ручной IPC для общения
Worker Threads:
+ Разделяемая память (SharedArrayBuffer)
+ Один процесс
- Crash одного убивает другие (нет изоляции)
+ Лучше для CPU-bound задач
PM2 -i max:
+ Тот же cluster под капотом
+ Не нужно писать код
+ Встроенный мониторинг и логи
+ Рекомендуется для production
Частые ошибки
- Хранить состояние в памяти воркера — разные воркеры имеют разную память; сессии, кеш — в Redis
- Не обрабатывать событие 'exit' — воркер умер и не перезапустился; сервер теряет ёмкость
- Использовать cluster вместо PM2 — ручное управление cluster сложнее и хуже поддерживается; в production предпочитать PM2
- Балансировка не гарантирована — ОС распределяет соединения неравномерно; для точной балансировки нужен nginx upstream
Связанные темы
Ресурсы
🎓 Источник: HTTP сервер на Node.js (routing, cluster, IP sticky)
- 📅 2018-10-17 · YouTube
- Тезисы:
- В кластере
http.createServer"реален" только в мастере; в воркерах его симулирует cluster. - Балансировка — round-robin: мастер раздаёт соединения по очереди.
- Process ID в заголовке ответа — простая диагностика того, какой воркер ответил.
- Альтернатива: child_process с отдельным портом на каждый воркер + клиентская балансировка.
- Sticky-session по IP:
workerId = ipToInt(remoteAddress) % numCPUs.
- В кластере
- Цитата: «Серверный сокет откроется в мастер-процессе, потом с ним свяжутся несколько воркеров. И когда запросы приходят в мастер, он при помощи RoundRobin раздаёт.»
🎓 Источник: Archive 2018 · Processes, Clustering and Balancing in Node.js
- 📅 2020-01-18 · YouTube
- Тезисы:
cluster.isMaster/cluster.isWorker(илиisPrimary) — два пути выполнения в одном файле.- До ~8 ядер cluster эффективен; на 20+ ядрах мастер упирается в 100% CPU.
- На большом числе ядер — переход на
child_process.fork(file)со своим сокетом на воркер. - Балансировку выносим на клиент (порт = base + workerId) или в L4-балансер.
cluster.forkсоздаёт IPC-канал между master и worker автоматически.
- Цитата: «Когда ядер становится много, мастер 100% в CPU, а воркеры там 10-20% и не могут нагрузиться.»
🎓 Источник: Высоконагруженные распределённые приложения на Node.js
- 📅 2018-10-30 · YouTube
- Тезисы:
- Предел одного процесса ноды — ~50 тысяч соединений; на сервер — 500к; на кластер — до 10М.
- Три метрики высокой нагрузки: connections, RPS, latency — измерять одновременно.
- HighLoad, HighConnectivity, HighInterconnect — разные оси нагрузки, разные оптимизации.
- Долгоживущие процессы → нельзя рестартить → graceful migration юзеров между процессами.
- Балансировку лучше выносить на клиент: хеш от IP, защита от DoS через consistent hashing.
🎓 Источник: Обзор встроенного Node.js API
- 📅 2018-09-26 · YouTube
- Тезисы:
- Cluster — обёртка над child_process с round-robin распределением соединений (на Linux)
- На Windows распределение OS-зависимое (IOCP)
- Cluster или child_process — выбор зависит от того, нужны ли разные кодовые базы или одна
🎓 Источник: Многопоточность в Metarhia
- 📅 2021-10-03 · YouTube · [Marp](../../Documents/TimurShemsedinov/2021-10-03 — 💻 Многопоточность в Metarhia (первый сервер приложений для Node.js с менеджером (BLLSuCYKLNk).md)
- Тезисы: менеджер потоков поверх cluster + worker_threads, шедулинг задач, balancing