Cluster: como usar todos os núcleos da sua CPU
Um núcleo a 100% e sete dormindo: o módulo cluster do Node.js em 20 linhas, os bônus (tolerância a falha, deploy sem downtime) e as pegadinhas que separam demo de produção.
Você provisionou um servidor de 8 núcleos para sua API Node.js. Abra o monitor de recursos num pico de tráfego e prepare-se para a cena do crime: um núcleo a 100%, sete dormindo. Você está pagando por oito e usando um.
Não é bug; é design. Como vimos no artigo do event loop, o JavaScript do Node roda numa única thread, portanto num único núcleo. O módulo cluster existe exatamente para essa conta fechar: um processo Node por núcleo, todos dividindo a mesma porta.
Como funciona por baixo
O modelo é primário/trabalhadores (primary/workers): o processo primário não atende requisições; ele forca N cópias da aplicação e distribui as conexões entre elas. Cada worker é um processo completo, com seu próprio event loop, sua própria memória e seu próprio V8.
O truque da porta compartilhada: quando um worker chama server.listen(3000), o Node percebe que ele é um worker e, em vez de abrir a porta, pede o socket ao primário. No Linux, por padrão, o primário aceita as conexões e as distribui em round-robin entre os workers (documentação oficial).
// server.js
const cluster = require("node:cluster");
const os = require("node:os");
if (cluster.isPrimary) {
const n = os.availableParallelism(); // núcleos disponíveis
console.log(`Primário ${process.pid}: subindo ${n} workers`);
for (let i = 0; i < n; i++) cluster.fork();
cluster.on("exit", (worker, code) => {
console.log(`Worker ${worker.process.pid} morreu (${code}). Repondo...`);
cluster.fork(); // auto-recuperação: worker que cai é substituído
});
} else {
// Cada worker roda a aplicação completa
require("./app"); // seu Express/Fastify com app.listen(3000)
}
Vinte linhas, e a API passa a usar a máquina inteira. Num servidor de 8 núcleos, o ganho de throughput em cargas típicas de API fica próximo do linear.
Os bônus que vêm de graça
- Tolerância a falha: uma exceção não tratada derruba um worker, não o serviço. O primário repõe em milissegundos, e os outros 7 seguem atendendo. É o MTTR despencando por arquitetura.
- Deploy sem downtime: reinicie os workers um por vez (rolling restart): o serviço nunca fica fora do ar durante o deploy. Ferramentas de processo como o PM2 empacotam isso num comando (
pm2 reload), com o modo cluster embutido (pm2.keymetrics.io). - Isolamento de bloqueio: aquele endpoint que bloqueia o event loop por 200ms agora trava 1/8 da capacidade, não 100%.

As pegadinhas (a parte que separa a demo da produção)
1. Nada de estado em memória. Cada worker tem sua própria memória. Sessão guardada num objeto? O usuário loga no worker 3 e "desloga" ao cair no worker 5. Cache em variável? Oito versões divergentes. A regra: estado compartilhado sai do processo: sessões e cache no Redis, e o processo fica descartável (como todo o resto).
2. WebSockets pedem sessão pegajosa. Handshakes de Socket.IO com polling precisam cair sempre no mesmo worker (sticky sessions), ou a conexão quebra. As soluções: adapter de Redis + sticky no balanceador, ou transporte só-websocket.
3. Multiplicou processos, multiplicou conexões. Oito workers × pool de 10 conexões de banco = 80 conexões. Dimensione o pool por worker dividindo o limite total, ou o banco recusa conexão no pico.
4. Memória se multiplica também. Cada worker carrega a aplicação inteira. App de 300MB × 8 = 2,4GB. Ajuste o número de workers à memória disponível, não só aos núcleos.
Cluster, Worker Threads ou Kubernetes?
A dúvida clássica, com resposta direta:
- Cluster: escala requisições (I/O) pelos núcleos de uma máquina. É o padrão para APIs.
- Worker Threads: escala cálculo (CPU) sem multiplicar o processo inteiro; ideal para tarefas pesadas pontuais (próximo artigo da série).
- Kubernetes/containers: escala em várias máquinas. E aqui um detalhe de arquitetura moderna: se você roda um container por pod com limite de 1 CPU, o cluster interno vira redundância; deixe o orquestrador replicar. Cluster brilha em VMs e máquinas dedicadas com muitos núcleos.
Os três se combinam: cluster dentro da VM, réplicas no orquestrador, workers para o cálculo pesado. O desenho certo depende de onde sua aplicação roda, exatamente o tipo de decisão que ajustamos no serviço de Desenvolvimento Web, Mobile e Desktop.
Quantos núcleos sua API está desperdiçando agora? Rode os.availableParallelism() e compare com seus processos. Depois conte para a IA da Rabelo Digital, aqui no canto da tela, e ela desenha o plano. Sem formulário, sem espera.


Deixe um Comentário