🧠FFVAcademy
⚖️

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.host

L4 vs L7: comparação e casos de uso

AspectoL4 (TCP/UDP)L7 (HTTP/HTTPS)
VisibilidadeIP, porta, protocolo TCPHeaders, URL, body, cookies
VelocidadeMais rápido (sem parsing)Overhead de parse HTTP
RoteamentoPor IP/porta apenasPor path, header, host, cookie
TLS terminationPassthrough (não inspeciona)Termina TLS, reinspeciona
WebSocketTransparenteRequer configuração explícita
Banco de dadosSim (qualquer protocolo)Não (sem protocolo DB)
WAF / rate limitNão possívelSim
ExemplosAWS NLB, HAProxy TCP, IPVSAWS 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