Что такое cluster в Node.js?

SeniorNode.js · Backend·Обновлено 26 июня 2026
Коротко
Модуль cluster позволяет создавать дочерние процессы (workers), которые разделяют один и тот же серверный порт, обходя однопоточность 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) вместо горизонтального масштабирования репликами

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее