15 min de leitura · Guia técnico
Escalar aplicações Node.js significa aumentar a capacidade de processamento para suportar mais usuários simultâneos sem degradar a performance. Para escalar Node.js quando o tráfego explodir, siga estes passos:
- Configure clustering com PM2 para usar todos os cores da CPU
- Implemente cache Redis para reduzir consultas ao banco
- Configure load balancer Nginx para distribuir requisições
- Otimize queries do banco de dados e conexões
- Configure monitoramento de performance em tempo real
- Implemente auto-scaling horizontal com múltiplos servidores
Pré-requisitos
- Servidor VPS ou dedicado com Ubuntu 22.04 LTS
- Node.js 18+ e npm instalados
- Aplicação Node.js funcionando em produção
- Acesso root via SSH ao servidor
- Redis Server para cache (opcional mas recomendado)
- Nginx para load balancing
Configurando clustering com PM2 para máxima performance
O clustering permite que aplicações Node.js utilizem todos os cores disponíveis da CPU, multiplicando a capacidade de processamento. O PM2 é a ferramenta mais robusta para gerenciar clusters Node.js em produção.
Primeiro, instale o PM2 globalmente:
npm install -g pm2
Crie um arquivo de configuração ecosystem.config.js na raiz do projeto:
module.exports = {
apps: [{
name: 'minha-app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
max_memory_restart: '1G',
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/combined.log'
}]
}
Inicie a aplicação em modo cluster:
pm2 start ecosystem.config.js
Output esperado:
┌─────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ minha-app│ default │ 1.0.0 │ cluster │ 12345 │ 0s │ 0 │ online │ 0% │ 25.2mb │ root │ disabled │
│ 1 │ minha-app│ default │ 1.0.0 │ cluster │ 12346 │ 0s │ 0 │ online │ 0% │ 25.1mb │ root │ disabled │
└─────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Verifique o status das instâncias:
pm2 status
pm2 monit
Implementando cache Redis para reduzir latência
O cache Redis elimina consultas repetitivas ao banco de dados, reduzindo drasticamente o tempo de resposta. Instale e configure o Redis no servidor:
sudo apt update
sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-server
Instale o cliente Redis na aplicação Node.js:
npm install redis
Configure o cliente Redis na aplicação:
const redis = require('redis');
const client = redis.createClient({
host: 'localhost',
port: 6379,
retry_strategy: (options) => {
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('Redis server recusou conexão');
}
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Tempo limite de retry excedido');
}
return Math.min(options.attempt * 100, 3000);
}
});
// Middleware de cache
const cacheMiddleware = (duration) => {
return async (req, res, next) => {
const key = req.originalUrl;
try {
const cached = await client.get(key);
if (cached) {
return res.json(JSON.parse(cached));
}
res.sendResponse = res.json;
res.json = (body) => {
client.setex(key, duration, JSON.stringify(body));
res.sendResponse(body);
};
next();
} catch (error) {
next();
}
};
};
Aplique cache em rotas específicas:
app.get('/api/produtos', cacheMiddleware(300), async (req, res) => {
const produtos = await db.query('SELECT * FROM produtos');
res.json(produtos);
});
Configurando load balancer Nginx para distribuição de carga
O Nginx atua como proxy reverso, distribuindo requisições entre múltiplas instâncias da aplicação. Instale o Nginx:
sudo apt install nginx -y
sudo systemctl enable nginx
Crie a configuração do load balancer em /etc/nginx/sites-available/minha-app:
upstream nodejs_backend {
least_conn;
server 127.0.0.1:3000 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3002 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3003 weight=1 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name meudominio.com;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
alias /var/www/minha-app/static/;
}
}
Ative a configuração:
sudo ln -s /etc/nginx/sites-available/minha-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Configure o PM2 para usar portas diferentes:
module.exports = {
apps: [{
name: 'app-3000',
script: './app.js',
env: { PORT: 3000 }
}, {
name: 'app-3001',
script: './app.js',
env: { PORT: 3001 }
}, {
name: 'app-3002',
script: './app.js',
env: { PORT: 3002 }
}, {
name: 'app-3003',
script: './app.js',
env: { PORT: 3003 }
}]
}
Otimizando conexões de banco de dados
Pool de conexões evita overhead de criar/destruir conexões constantemente. Configure pools adequados para sua aplicação:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'app_user',
password: 'senha_segura',
database: 'minha_app',
connectionLimit: 20,
queueLimit: 0,
acquireTimeout: 60000,
timeout: 60000,
reconnect: true
});
// Middleware para queries otimizadas
const executeQuery = async (sql, params = []) => {
const connection = await pool.getConnection();
try {
const [results] = await connection.execute(sql, params);
return results;
} finally {
connection.release();
}
};
Atenção: Monitore o número de conexões ativas para evitar esgotar o pool durante picos de tráfego.
Implemente índices adequados nas tabelas mais consultadas:
-- Exemplo de índices para otimização
CREATE INDEX idx_usuario_email ON usuarios(email);
CREATE INDEX idx_produto_categoria ON produtos(categoria_id);
CREATE INDEX idx_pedido_data ON pedidos(data_criacao);
Implementando monitoramento de performance
Configure monitoramento completo para detectar gargalos antes que afetem usuários. Instale ferramentas de monitoramento:
npm install express-status-monitor
npm install newrelic
Configure o monitor de status na aplicação:
const monitor = require('express-status-monitor');
app.use(monitor({
title: 'Minha App Status',
path: '/status',
spans: [{
interval: 1,
retention: 60
}, {
interval: 5,
retention: 60
}],
chartVisibility: {
cpu: true,
mem: true,
load: true,
responseTime: true,
rps: true,
statusCodes: true
},
healthChecks: [{
protocol: 'http',
host: 'localhost',
path: '/health',
port: '3000'
}]
}));
Configure alertas no PM2:
pm2 install pm2-server-monit
pm2 set pm2-server-monit:refresh 2000
pm2 set pm2-server-monit:cpu_threshold 80
pm2 set pm2-server-monit:mem_threshold 80
Crie endpoint de health check:
app.get('/health', async (req, res) => {
try {
// Verifica conexão com banco
await pool.execute('SELECT 1');
// Verifica Redis
await client.ping();
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage()
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message
});
}
});
Configurando auto-scaling horizontal
Para tráfego extremo, configure múltiplos servidores com auto-scaling. Use Docker para facilitar deployment:
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["pm2-runtime", "start", "ecosystem.config.js"]
Configure docker-compose para múltiplas instâncias:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
deploy:
replicas: 4
resources:
limits:
memory: 512M
reservations:
memory: 256M
environment:
- NODE_ENV=production
- REDIS_URL=redis://redis:6379
depends_on:
- redis
- db
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
Para auto-scaling baseado em métricas, configure scripts de monitoramento:
#!/bin/bash
# auto-scale.sh
CPU_THRESHOLD=80
CURRENT_CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
if (( $(echo "$CURRENT_CPU > $CPU_THRESHOLD" | bc -l) )); then
echo "CPU alto detectado: $CURRENT_CPU%. Escalando..."
docker-compose up --scale app=6 -d
else
echo "CPU normal: $CURRENT_CPU%"
fi
Problemas comuns e como resolver
Aplicação trava durante picos de tráfego
Causa: Event loop bloqueado por operações síncronas ou CPU intensivas.
Solução: Identifique operações bloqueantes com clinic.js e mova processamento pesado para workers separados usando worker_threads ou filas como Bull Queue.
Memória aumenta constantemente até crash
Causa: Memory leaks em closures, event listeners não removidos ou cache sem expiração.
Solução: Use --inspect para debugging, configure max_memory_restart no PM2 e implemente limpeza adequada de recursos com process.on('SIGTERM').
Conexões de banco esgotam rapidamente
Causa: Pool de conexões mal dimensionado ou conexões não liberadas adequadamente.
Solução: Aumente connectionLimit gradualmente, sempre use finally para liberar conexões e monitore conexões ativas com SHOW PROCESSLIST.
Load balancer retorna 502 Bad Gateway
Causa: Instâncias Node.js não respondem ou portas incorretas no upstream.
Solução: Verifique se todas as instâncias estão rodando com pm2 status, confirme portas no arquivo de configuração do Nginx e teste conectividade com curl localhost:3000.
Cache Redis não funciona corretamente
Causa: Configuração incorreta do cliente ou chaves de cache mal estruturadas.
Solução: Verifique conexão com redis-cli ping, implemente tratamento de erro adequado e use TTL apropriado para evitar dados obsoletos.
Perguntas frequentes sobre escalar aplicações Node.js
Como o PM2 ajuda a escalar aplicações Node.js?
O PM2 permite executar múltiplas instâncias da aplicação Node.js em modo cluster, distribuindo a carga entre os cores da CPU. Ele também oferece restart automático, monitoramento de recursos e balanceamento de carga nativo.
Qual a diferença entre clustering e load balancing para Node.js?
Clustering executa múltiplas instâncias da aplicação no mesmo servidor, aproveitando todos os cores da CPU. Load balancing distribui requisições entre diferentes servidores ou instâncias, podendo usar clustering internamente em cada servidor.
Quando devo usar cache Redis em aplicações Node.js?
Use Redis quando sua aplicação faz consultas frequentes ao banco de dados, precisa armazenar sessões de usuário ou dados temporários. O Redis reduz drasticamente o tempo de resposta e alivia a carga do banco principal.
Como monitorar performance de aplicações Node.js em produção?
Use ferramentas como PM2 Monit, New Relic ou DataDog para acompanhar CPU, memória, tempo de resposta e erros. Configure alertas para picos de uso e monitore logs de erro em tempo real.
É possível escalar Node.js verticalmente sem clustering?
Sim, mas é limitado. Escalabilidade vertical aumenta recursos do servidor (CPU, RAM), mas Node.js por padrão usa apenas um core. Clustering é essencial para aproveitar múltiplos cores e maximizar performance.
Conclusão
- Configure PM2 em modo cluster para aproveitar todos os cores da CPU disponíveis
- Implemente cache Redis estratégico para reduzir latência e carga no banco de dados
- Use Nginx como load balancer para distribuir tráfego entre múltiplas instâncias da aplicação
Leia também
- Instalando painel de gerenciamento de hospedagem VirtualMin.
- Como usar a ferramenta oficial de acesso remoto do Windows no PC e celular
- Como acessar o painel de gerenciamento dos meus Serviços.
Precisa de ajuda para escalar sua aplicação Node.js?
Nossa equipe especializada pode configurar a infraestrutura ideal para suportar qualquer volume de tráfego. Oferecemos servidores VPS otimizados com recursos dedicados para aplicações Node.js de alta performance.