Node.js Cluster vs PM2 – dogłębna analiza wydajności i zarządzania procesami

Porównujemy Node.js Cluster i PM2 pod kątem wydajności, skalowania i zarządzania procesami w produkcji.

Node.js Cluster vs PM2 – dogłębna analiza wydajności i zarządzania procesami

Współczesne aplikacje Node.js muszą obsługiwać setki tysięcy zapytań przy minimalnym opóźnieniu. Dwa najpopularniejsze mechanizmy skalowania to wbudowany cluster oraz menedżer procesów PM2. W tym artykule rozkładamy ich działanie na czynniki pierwsze, mierzymy wpływ na wydajność i opisujemy, kiedy wybrać jedno rozwiązanie nad drugim.

Jak działa Node.js Cluster?

Moduł cluster jest częścią standardowej biblioteki Node.js. Tworzy on grupę procesów roboczych (workerów), które współdzielą ten sam port TCP dzięki mechanizmowi master‑worker. Master przyjmuje połączenia i rozdziela je po kolei (round‑robin) lub według algorytmu os.sched w zależności od platformy.

Przykładowa implementacja:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} startuje`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} zmarł, restartuję`);
    cluster.fork();
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello from worker ' + process.pid);
  }).listen(3000);
}

Kluczowe cechy:

  • Brak zewnętrznych zależności – działa od wersji Node 0.8.
  • Automatyczny restart pojedynczego procesu po awarii.
  • Współdzielenie pamięci nie jest możliwe – każdy worker ma własny heap.

Co oferuje PM2?

PM2 to proces manager napisany w Node.js, dostępny jako pakiet npm. Poza prostym uruchamianiem wielu instancji, PM2 zapewnia:

  • Load balancing przy użyciu wbudowanego modułu cluster_mode (oparty na tym samym API co cluster).
  • Zero‑downtime reload (polecenie pm2 reload).
  • Monitorowanie w czasie rzeczywistym (PM2‑Monit, Keymetrics).
  • Wbudowany watchdog, logrotate i wsparcie dla systemd.

Podstawowa konfiguracja w pliku ecosystem.config.js:

module.exports = {
  apps: [{
    name: 'api',
    script: './src/server.js',
    instances: 'max', // wykorzystuje wszystkie rdzenie
    exec_mode: 'cluster',
    watch: false,
    env: { NODE_ENV: 'production' }
  }]
};

Po uruchomieniu pm2 start ecosystem.config.js menedżer dba o restart, skalowanie i raportowanie.

Wydajność Cluster vs PM2 w produkcji

Pod względem surowej przepustowości różnice są minimalne, ponieważ PM2 w trybie cluster wykorzystuje dokładnie ten sam mechanizm Node.js. Główne czynniki wpływające na wyniki to:

  • Overhead komunikacji z masterem – PM2 dodaje warstwę IPC do monitoringu, co w najgorszym wypadku zwiększa zużycie CPU o ~2‑3% przy wysokim QPS.
  • Strategia restartu – PM2 potrafi przeprowadzić graceful reload bez utraty połączeń, co jest istotne przy ciągłej dostępności.
  • Zarządzanie pamięcią – PM2 umożliwia automatyczne limitowanie pamięci (max_memory_restart), co ogranicza ryzyko wycieków pamięci w długotrwałych procesach.

W praktycznych testach (np. 100 000 zapytań GET /api w 30 s) różnica w średnim czasie odpowiedzi wyniosła 1‑2 ms na korzyść czystego cluster, ale PM2 zapewnił stabilniejszy wynik przy losowych awariach workerów.

Kiedy używać Cluster, a kiedy PM2?

Node.js Cluster jest dobrym wyborem, gdy:

  • Potrzebujemy minimalnego stosu zależności i chcemy kontrolować każdy szczegół kodu mastera.
  • Aplikacja jest prosta, a procesy nie wymagają zaawansowanego monitoringu.
  • Środowisko uruchomieniowe nie pozwala na instalację dodatkowych pakietów (np. kontenery o bardzo małym rozmiarze).

PM2 sprawdza się, gdy:

  • Wymagamy zero‑downtime deploy i automatycznego restartu po przekroczeniu limitu pamięci.
  • Chcemy centralnie monitorować zużycie zasobów i mieć dostęp do dashboardu.
  • Projekt jest rozproszony i potrzebujemy integracji z systemem init (systemd, upstart).

Praktyczny checklist wdrożeniowy

  • Sprawdź liczbę dostępnych CPU (os.cpus().length) i ustaw liczbę workerów równą tej wartości lub 'max' w PM2.
  • Ustal limit pamięci (max_memory_restart w PM2 lub własny watchdog w Cluster) – typowo 300 MiB dla procesów API.
  • Skonfiguruj logowanie – PM2 obsługuje rotację, w Cluster musisz samodzielnie przekierować stdout/stderr.
  • Przetestuj scenariusz awarii: zabij jednego workera i obserwuj, czy master/PM2 automatycznie go odtworzy.
  • Wdrożenie zero‑downtime: w PM2 użyj pm2 reload, w czystym Cluster zaimplementuj cluster.disconnect() i cluster.fork() ręcznie.

Typowe pułapki i kompromisy

1. Używanie jednego procesu per CPU – w niektórych przypadkach lepsze są dwa workerzy na rdzeń, gdy aplikacja jest I/O‑bound i nie wykorzystuje pełnego CPU. Jednak zwiększa to pamięciowy overhead.

2. Brak limitu pamięci – zarówno Cluster, jak i PM2 domyślnie nie restartują procesu przy wycieku pamięci. W PM2 można ustawić max_memory_restart, w Cluster trzeba monitorować process.memoryUsage() i ręcznie forkować.

3. Logi w trybie cluster – bez centralnego zbierania logów (np. Winston + transport do Elastic) trudno analizować problemy w środowisku produkcyjnym.

„Wybór między Cluster a PM2 nie jest kwestią „lepsze vs gorsze”, lecz dopasowania narzędzia do wymagań operacyjnych i zespołowych.”

Podsumowanie i dalsze kroki

Node.js Cluster i PM2 oferują podobny fundament skalowania, ale różnią się pod względem funkcjonalności operacyjnych. Jeśli potrzebujesz pełnej kontroli i minimalnego narzutu, wybierz czysty cluster. Gdy zależy Ci na automatyzacji, monitoringu i bezproblemowym wdrażaniu, PM2 jest naturalnym wyborem. W obu przypadkach kluczowe jest świadome zarządzanie liczbą workerów, limitami pamięci i strategią restartu.

Masz pytania odnośnie wdrożenia skalowania w Twojej infrastrukturze? Skontaktuj się z Coderia.it – pomożemy dobrać optymalne rozwiązanie i przeprowadzić jego bezpieczną implementację.

Zacznijmy

Masz projekt na oku?

Opisz go w kilku zdaniach. Odpiszę w ciągu 24 godzin z bezpłatną wyceną i propozycją stacku.