Что такое cluster в Node.js?
Что такое cluster в Node.js
Node.js работает в однопоточном режиме — один процесс использует одно ядро CPU. На современных серверах с 8–32 ядрами это означает, что большая часть вычислительных ресурсов простаивает. Модуль cluster из стандартной библиотеки решает эту проблему, позволяя запускать несколько экземпляров Node.js-приложения, каждый из которых работает в своём процессе.
Архитектура master-worker
Кластер строится по схеме master-worker (в новых версиях — primary-worker):
- Primary (master) — главный процесс, который форкает дочерние процессы и управляет ими. Сам не обрабатывает входящие запросы.
- Workers — дочерние процессы, каждый со своим event loop, памятью и стеком. Реально обрабатывают HTTP-запросы.
Все workers разделяют один и тот же порт благодаря механизму передачи файловых дескрипторов между процессами через IPC-канал. Primary принимает входящее соединение и передаёт его одному из workers.
Балансировка нагрузки
По умолчанию на всех платформах (кроме Windows) используется алгоритм round-robin: primary распределяет соединения между workers по очереди. На Windows ОС сама решает, какому процессу передать соединение.
Коммуникация между процессами
Primary и workers общаются через IPC с помощью process.send() и события message. Это позволяет передавать команды, собирать метрики и координировать работу.
Отказоустойчивость
Если worker падает (необработанное исключение, OOM), primary получает событие exit и может немедленно поднять новый worker, обеспечивая непрерывную работу сервиса.
Пример использования
См. пример кода ниже.
Ограничения cluster
- Нет общей памяти — каждый worker изолирован. Сессии, кэш, состояние — всё должно храниться во внешнем хранилище (Redis, БД).
- Overhead форка — каждый worker потребляет столько же памяти, сколько основной процесс.
- IPC не для больших объёмов — передача больших данных через
process.send()медленная.
cluster vs PM2 vs worker_threads
| cluster | PM2 cluster mode | worker_threads | |
|---|---|---|---|
| Изоляция | Процессы | Процессы | Потоки |
| Общая память | Нет | Нет | SharedArrayBuffer |
| Управление | Ручное | Автоматическое | Ручное |
| Назначение | HTTP-серверы | Продакшн-деплой | CPU-задачи |
Для CPU-intensive задач (шифрование, обработка изображений) правильный выбор — worker_threads, которые используют общую память через SharedArrayBuffer. cluster оптимален для масштабирования I/O-bound HTTP-серверов.
Что хочет услышать интервьюер
Понимание того, что Node.js однопоточный и cluster решает проблему недоиспользования многоядерных CPU
Знание архитектуры primary-worker и того, как workers разделяют один порт через IPC
Осознание ограничений: отсутствие общей памяти, необходимость внешнего хранилища для состояния
Умение сравнить cluster с worker_threads и PM2 — когда что применять
Понимание механизма балансировки (round-robin) и отказоустойчивости через перезапуск упавших workers
Пример: Базовый HTTP-сервер с cluster
import cluster from 'node:cluster';
import http from 'node:http';
import os from 'node:os';
const NUM_WORKERS = os.availableParallelism();
if (cluster.isPrimary) {
console.log(`Primary PID ${process.pid} запущен, форкаем ${NUM_WORKERS} workers`);
// Запускаем по одному worker на каждое ядро
for (let i = 0; i < NUM_WORKERS; i++) {
cluster.fork();
}
// Перезапускаем worker при падении
cluster.on('exit', (worker, code, signal) => {
console.warn(`Worker PID ${worker.process.pid} упал (code=${code}, signal=${signal}), перезапускаем...`);
cluster.fork();
});
// Получаем метрики от workers
cluster.on('message', (worker, message) => {
if (message.type === 'metric') {
console.log(`Worker ${worker.id}: ${JSON.stringify(message.data)}`);
}
});
} else {
// Каждый worker создаёт свой HTTP-сервер на том же порту
const server = http.createServer((req, res) => {
// Отправляем метрику в primary
process.send?.({ type: 'metric', data: { url: req.url, pid: process.pid } });
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Ответ от worker PID ${process.pid}\n`);
});
server.listen(3000, () => {
console.log(`Worker PID ${process.pid} слушает порт 3000`);
});
// Обрабатываем команду graceful shutdown от primary
process.on('message', (msg: unknown) => {
if ((msg as { type: string }).type === 'shutdown') {
server.close(() => process.exit(0));
}
});
}
Типичные ошибки
Путают cluster с worker_threads — думают, что cluster подходит для CPU-intensive задач и даёт общую память
Не учитывают отсутствие общего состояния: хранят сессии в памяти процесса и удивляются, что запросы не авторизованы
Запускают worker на каждое ядро без учёта того, что приложение I/O-bound и bottleneck — не CPU
Не реализуют перезапуск упавших workers, теряя отказоустойчивость
Используют cluster в контейнерных средах (Kubernetes) вместо горизонтального масштабирования репликами


