Utilizando Cluster no Node.js para Processamento Paralelo

O módulo cluster do Node.js possibilita criar múltiplos processos filhos (workers) que compartilham uma mesma porta de servidor. Essa abordagem permite explorar ao máximo CPUs com vários núcleos, melhorando o desempenho e a resiliência da aplicação.

Como Funciona Internamente

Baseado no child_process, o módulo cluster opera com um processo principle (master) e processos trabalhadores (workers):

  • Processo principle (master): Responsável por gerenciar e distribuir tarefas para os workers.
  • Processos trabalhadores (workers): Executam o código do servidor e processam as requisições.

Existem duas formas de lidar com as conexões no cluster:

  • Roteamento via master: O master escuta a porta, recebe a requisição HTTP e a encaminha via IPC para um worker. O worker processa e devolve o resultado ao master, que o retorna ao cliente.
  • Conexão direta: O master apenas notifica o worker, que estabelece uma conexão direta com o cliente.

Embora a segunda pareça mais eficiente, na prática o roteamento via master costuma oferecer melhor balanceamento de carga devido a limitações de escalonamento do sistema operacional. (ref)

Passos Básicos para Usar o Cluster

  1. Verificar se o processo atual é o principal usando cluster.isPrimary (ou cluster.isMaster).
  2. No processo principal, criar workers com cluster.fork().
  3. Nos workers, executar o código do servidor (ex: HTTP).

Exemplo prático:

const cluster = require("node:cluster");
const http = require("node:http");
const totalCpus = require("node:os").cpus().length;

if (cluster.isPrimary) {
  console.log(`Processo principal PID: ${process.pid} em execução`);

  // Cria um worker para cada núcleo da CPU
  for (let i = 0; i < totalCpus; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker com PID ${worker.process.pid} foi encerrado`);
  });
} else {
  // Código executado pelos workers
  http.createServer((req, res) => {
    let pid = process.pid;
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end(`Resposta do worker ${pid}\n`);
  }).listen(8000);

  console.log(`Worker PID ${process.pid} iniciado`);
}

Vantagens do Uso de Cluster

  • Melhor desempenho: Aproveita múltiplos núcleos do processador para aumentar a capacidade de atender requisições simultâneas.
  • Alta disponibilidade: Se um worker falhar, os demais continuam operando e o master pode recriá-lo.
  • Balanceamento automático: Os workers compartilham a mesma porta, distribuindo as requisições de forma transparente.

Pergunta para Reflexão

Considere um servidor HTTP que escuta na porta 8000. Com o cluster, é possível rodar múltiplos workers escutando na mesma porta. Porém, se você executar o código abaixo duas vezes em processos independentes, o segundo lançará um erro "porta já em uso". Por que isso acontece?

// servidor.js
const http = require("node:http");
http.createServer((req, res) => {
  let pid = process.pid;
  res.writeHead(200);
  res.end(`Worker PID: ${pid}\n`);
}).listen(8000);

Deixe sua opinião nos comentários. Se o post atingir 20 curtidas, publicarei a explicação detahlada.

Tags: Node.js cluster child_process IPC HTTP

Publicado em 7-21 15:36