⚖️
Proxies, reverse proxies, load balancers L4 vs L7
⏱ 14 min de leitura·+70 XP
Entre o cliente e o servidor existem múltiplas camadas de intermediários — cada um com responsabilidade específica. Entender proxies e load balancers é o que diferencia "sobe numa VPS" de "opera em alta disponibilidade".
Forward proxy vs reverse proxy
# Forward proxy: serve o CLIENTE (fala em nome dos clientes)
# Cliente → [Forward Proxy] → Internet
# Servidor vê: IP do proxy, não do cliente
# Reverse proxy: serve o SERVIDOR (fala em nome dos servidores)
# Internet → [Reverse Proxy] → Backends
# Cliente vê: IP do reverse proxy, não do backend
# Nginx como reverse proxy simples:
nginx_config = """
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://localhost:8000; # encaminha para 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;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
"""
# X-Forwarded-For: lista de IPs que o request atravessou
# "X-Forwarded-For: 203.0.113.1, 10.0.0.1"
# 203.0.113.1 = IP real do cliente
# 10.0.0.1 = IP do primeiro proxy interno
# Cuidado: X-Forwarded-For pode ser forjado pelo cliente!
# Confie apenas no ÚLTIMO IP adicionado por um proxy CONFIÁVEL
# AWS ALB adiciona o IP real do cliente como último entry
# Verificar IP real do cliente em Flask/FastAPI:
from fastapi import Request
def get_real_ip(request: Request) -> str:
"""Extrai IP real do cliente considerando proxies."""
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
# O IP mais à esquerda é o do cliente (mas pode ser forjado)
# Em produção: confie apenas em IPs adicionados por seu proxy
return forwarded_for.split(",")[0].strip()
return request.client.hostL4 vs L7: comparação e casos de uso
| Aspecto | L4 (TCP/UDP) | L7 (HTTP/HTTPS) |
|---|---|---|
| Visibilidade | IP, porta, protocolo TCP | Headers, URL, body, cookies |
| Velocidade | Mais rápido (sem parsing) | Overhead de parse HTTP |
| Roteamento | Por IP/porta apenas | Por path, header, host, cookie |
| TLS termination | Passthrough (não inspeciona) | Termina TLS, reinspeciona |
| WebSocket | Transparente | Requer configuração explícita |
| Banco de dados | Sim (qualquer protocolo) | Não (sem protocolo DB) |
| WAF / rate limit | Não possível | Sim |
| Exemplos | AWS NLB, HAProxy TCP, IPVS | AWS ALB, Nginx, Envoy, Traefik |
# Nginx como load balancer L7:
nginx_upstream_config = """
upstream api_backends {
# Algoritmos de balanceamento:
# (sem declaração) = round robin ← padrão, simples
# least_conn; ← menor número de conexões ativas
# ip_hash; ← sticky por IP do cliente
# random two least_conn; ← escolhe 2 aleatórios, menor conexão
server backend1:8000 weight=3; # recebe 3x mais tráfego
server backend2:8000 weight=1;
server backend3:8000 backup; # só recebe se os outros falharem
keepalive 32; # pool de conexões persistentes para os backends
}
server {
listen 443 ssl http2;
# Roteamento por path (L7):
location /api/ {
proxy_pass http://api_backends;
}
location /static/ {
root /var/www; # serve arquivos locais (sem proxy)
expires 1y;
add_header Cache-Control "public, immutable";
}
# Roteamento por header (canary deployment):
location /v2/ {
if ($http_x_version = "beta") {
proxy_pass http://beta_backend;
break;
}
proxy_pass http://stable_backend;
}
}
"""
# HAProxy como L4/L7:
haproxy_config = """
frontend http_in
bind *:80
mode http
default_backend app_servers
backend app_servers
mode http
balance roundrobin
option httpchk GET /health
http-check expect status 200
server app1 10.0.0.1:8000 check inter 2s fall 3 rise 2
server app2 10.0.0.2:8000 check inter 2s fall 3 rise 2
server app3 10.0.0.3:8000 check inter 2s fall 3 rise 2
"""Health checks e algoritmos de balanceamento
# Health checks: detectar backends doentes antes que usuários sofram
from fastapi import FastAPI
import time
import psutil
app = FastAPI()
# Endpoint de health check — deve ser leve e rápido:
@app.get("/health")
def health():
return {"status": "ok", "ts": time.time()}
# Endpoint de readiness (K8s) — verifica dependências:
@app.get("/ready")
async def ready():
checks = {}
# Verificar banco de dados:
try:
# await db.execute("SELECT 1")
checks["database"] = "ok"
except Exception as e:
checks["database"] = f"error: {e}"
# Verificar Redis:
try:
# await redis.ping()
checks["redis"] = "ok"
except Exception as e:
checks["redis"] = f"error: {e}"
all_ok = all(v == "ok" for v in checks.values())
status = 200 if all_ok else 503
return checks # retorna 200 = pronto, 503 = não pronto
# Algoritmos de balanceamento:
# Round Robin: distribui sequencialmente — simples, funciona bem se requests são iguais
# Weighted RR: versão com pesos — útil quando backends têm capacidades diferentes
# Least Conn: envia para backend com menos conexões ativas — ideal para long-polling
# Random: escolha aleatória — simples, funciona bem estatisticamente
# IP Hash: hash do IP do cliente → mesmo backend (sticky, frágil)
# Consistent Hash: hash do request key → mesmo backend (estável com adição/remoção)
# Consistent hashing (usado em Redis Cluster, Cassandra, CDNs):
import hashlib
class ConsistentHashRing:
def __init__(self, nodes: list, replicas: int = 100):
self.ring = {}
self.sorted_keys = []
for node in nodes:
for i in range(replicas):
key = self._hash(f"{node}:{i}")
self.ring[key] = node
self.sorted_keys.append(key)
self.sorted_keys.sort()
def _hash(self, s: str) -> int:
return int(hashlib.md5(s.encode()).hexdigest(), 16)
def get_node(self, key: str) -> str:
if not self.ring:
return None
h = self._hash(key)
for ring_key in self.sorted_keys:
if h <= ring_key:
return self.ring[ring_key]
return self.ring[self.sorted_keys[0]] # wrap around
ring = ConsistentHashRing(["backend1", "backend2", "backend3"])
print(ring.get_node("user:123")) # sempre o mesmo backend para este user✅
Modelo mental: forward proxy = fala em nome dos clientes (filtro corporativo, privacidade). Reverse proxy = fala em nome dos servidores (TLS termination, cache, LB). L4 = mais rápido, qualquer protocolo TCP/UDP, sem inspeção de payload. L7 = roteamento por conteúdo HTTP, WAF, rate limiting. Sticky sessions são uma muleta — a solução certa é state externo (Redis). Health checks (fall 3, rise 2) garantem que backends doentes saem do pool antes de usuários sofrerem.
💡
Próximo: WebSocket, SSE e streaming — comunicação bidirecional e push do servidor para o cliente.
🧩
Quiz rápido
3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito
Continue lendo