Guia Completo Escalabilidade Web
Resposta direta: system design escala por gargalo medido — cache, filas e shards sem métrica são cosmética.
Escalando Aplicações Web Do Zero ao Milhão
Domine os conceitos de system design: microserviços, load balancing, caching, sharding e arquiteturas distribuídas para aplicações que suportam milhões de usuários.
Fundamentos da Arquitetura Escalável
Entenda como evoluir de uma arquitetura básica para uma estrutura resiliente e escalável por meio da separação de responsabilidades e eliminação de pontos únicos de falha.
Separação de Serviços
Aplicação e banco de dados devem estar em servidores distintos. Isso evita disputa por recursos e melhora a escalabilidade individual de cada parte.
Redundância e Alta Disponibilidade
Uma arquitetura robusta deve ter capacidade de failover automático com múltiplas instâncias para tolerância a falhas.
Técnicas Essenciais
Componentes cruciais para rodar aplicações robustas e altamente escaláveis
Escala Horizontal
Crie múltiplas instâncias da sua aplicação para distribuir requisições e eliminar gargalos
Load Balancer
Distribui o tráfego entre servidores automaticamente e melhora a performance
Isolamento de Falhas
Evite que falhas em um ponto prejudiquem toda a aplicação
Estratégias de Escalabilidade
Use as estratégias certas no momento certo para dar um passo além em sua arquitetura.
Escala Vertical
Adiciona mais recursos (CPU, memória) ao servidor. Funciona por um tempo, mas tem limite físico e não elimina pontos únicos de falha.
Escala Horizontal
Duplica instâncias da aplicação em servidores diferentes para distribuir requisições e garantir alta disponibilidade.
Arquitetura Prática
Implementações reais de padrões de escalabilidade usando Node.js, Docker e Kubernetes.
Load Balancer com NGINX
# nginx.conf
upstream backend {
server 192.168.1.10:3000 weight=3;
server 192.168.1.11:3000 weight=2;
server 192.168.1.12:3000 weight=1;
server 192.168.1.13:3000 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Health check
proxy_connect_timeout 3s;
proxy_send_timeout 3s;
proxy_read_timeout 3s;
}Configuração de load balancer com peso diferenciado e servidor backup para failover automático. Inclui health checks e timeouts para detecção rápida de falhas.
Cache Distribuído com Redis
const redis = require('redis');
const client = redis.createCluster({
rootNodes: [
{ host: 'redis-1.cluster.local', port: 6379 },
{ host: 'redis-2.cluster.local', port: 6379 },
{ host: 'redis-3.cluster.local', port: 6379 }
],
defaults: {
socket: {
reconnectStrategy: (retries) => Math.min(retries * 50, 500)
}
}
});
// Cache com TTL automático
const cacheUser = async (userId, userData) => {
const key = `user:${userId}`;
await client.setEx(key, 3600, JSON.stringify(userData)); // 1 hora
};
// Cache invalidation pattern
const invalidateUserCache = async (userId) => {
const patterns = [`user:${userId}`, `user:${userId}:*`];
for (const pattern of patterns) {
const keys = await client.keys(pattern);
if (keys.length > 0) await client.del(keys);
}
};{
Sistema de cache distribuído com cluster Redis, incluindo estratégias de TTL, invalidação de cache e recuperação automática de conexões.
Microserviços Arquitetura
Service Discovery
// consul-service.js
const consul = require('consul')();
const registerService = async (name, port) => {
const serviceId = `${name}-${process.env.HOSTNAME}`;
await consul.agent.service.register({
id: serviceId,
name: name,
port: port,
address: process.env.HOSTNAME,
check: {
http: `http://${process.env.HOSTNAME}:${port}/health`,
interval: '10s',
timeout: '3s',
deregistercriticalserviceafter: '30s'
}
});
console.log(`Service ${name} registered with ID: ${serviceId}`);
};
const discoverService = async (serviceName) => {
const services = await consul.health.service(serviceName);
return services[0].map(s => ({
host: s.Service.Address,
port: s.Service.Port
}));
};
Circuit Breaker
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 10000;
this.failureCount = 0;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async call(fn, ...args) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn(...args);
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.resetTimeout;
}
}
}
Database Scaling
Sharding Strategy
// sharding-strategy.js
const getShardKey = (userId) => {
// Consistent hashing para distribuição uniforme
const hash = require('crypto').createHash('md5').update(userId.toString()).digest('hex');
return parseInt(hash.substring(0, 8), 16) % SHARD_COUNT;
};
const shardConfig = {
0: { host: 'db-shard-0.cluster.local', port: 5432 },
1: { host: 'db-shard-1.cluster.local', port: 5432 },
2: { host: 'db-shard-2.cluster.local', port: 5432 },
3: { host: 'db-shard-3.cluster.local', port: 5432 }
};
const getConnection = (userId) => {
const shardId = getShardKey(userId);
return connectionPool[shardId];
};
// Query cross-shard para agregações
const aggregateAcrossShards = async (query) => {
const promises = Object.keys(shardConfig).map(shardId => {
const conn = connectionPool[shardId];
return conn.query(query);
});
const results = await Promise.all(promises);
return results.reduce((acc, result) => acc.concat(result.rows), []);
};
Read Replicas com Failover
const dbConfig = {
master: { host: 'db-master.cluster.local', port: 5432 },
replicas: [
{ host: 'db-replica-1.cluster.local', port: 5432 },
{ host: 'db-replica-2.cluster.local', port: 5432 },
{ host: 'db-replica-3.cluster.local', port: 5432 }
]
};
class DatabaseRouter {
constructor() {
this.replicaIndex = 0;
this.healthCheck();
}
async write(query, params) {
return await this.masterConnection.query(query, params);
}
async read(query, params) {
const replica = this.getHealthyReplica();
try {
return await replica.query(query, params);
} catch (error) {
console.warn('Replica failed, falling back to master');
return await this.masterConnection.query(query, params);
}
}
getHealthyReplica() {
// Round-robin entre réplicas saudáveis
const healthy = this.replicas.filter(r => r.healthy);
if (healthy.length === 0) return this.masterConnection;
const replica = healthy[this.replicaIndex % healthy.length];
this.replicaIndex++;
return replica;
}
async healthCheck() {
setInterval(async () => {
for (const replica of this.replicas) {
try {
await replica.query('SELECT 1');
replica.healthy = true;
} catch (error) {
replica.healthy = false;
}
}
}, 5000);
}
Links Referência
System Design Resources
- System Design Primer - Guia completo no GitHub
- NGINX Load Balancing - Documentação oficial
- Redis Scaling - Clustering e sharding
- Microservices Patterns - Padrões de arquitetura
Ferramentas e Tecnologias
- Kubernetes - Orquestração de containers
- Consul - Service discovery
- Prometheus - Monitoring e alertas
- PostgreSQL HA - Alta disponibilidade
Leitura relacionada: Curso Node.js · Clean Vertical Slice · Programa CrazyStack · Cursos.
Perguntas frequentes
Por onde estudar?
Request path, data model, falhas e custo.
Preciso de K8s no dia 1?
Não. Escala vertical + bom schema resolvem longe.
Node escala?
Sim com modelo certo e I/O não bloqueante bem usado.
Como práticar?
Desenhe e critique sistemas reais que você usa.
Continue: Curso Node.js · Clean Vertical Slice · Programa CrazyStack · Cursos.