Redes Distribuidas
Video da aula estara disponivel em breve
CDN: Content Delivery Networks
Uma CDN replica conteúdo em servidores distribuídos geograficamente (edge servers). Quando um usuário acessa o conteúdo, ele é servido pelo servidor mais próximo, reduzindo latência. CDNs são fundamentais para a Internet moderna — praticamente todo site de grande escala usa uma.
Sem CDN: Com CDN (ex: Cloudflare):
Brasil ---[200ms]---> US Brasil ---[5ms]---> Edge SP
(servidor unico nos EUA) |
|-- cache hit? Serve!
|-- cache miss? Busca no origin
CDN funciona com:
1. DNS retorna IP do edge server mais proximo (anycast)
2. Edge server verifica cache local
3. Cache hit: serve diretamente (latencia minima)
4. Cache miss: busca no origin server, cacheia, serve
Anycast routing (como CDN encontra o server mais próximo):
┌─────────────┐
│ Mesmo IP: │ Usuário em SP → roteado para Edge SP
│ 104.16.1.1 │ Usuário em NY → roteado para Edge NY
│ │ Usuário em Tóquio → roteado para Edge JP
└─────────────┘
O BGP roteia para o datacenter mais próximo
porque múltiplos datacenters anunciam o mesmo IP
Políticas de cache e invalidação
O desafio central de uma CDN é cache invalidation — como garantir que o conteúdo no edge está atualizado?
Estratégia Header HTTP Comportamento
───────────── ──────────────────── ─────────────────────────────
TTL fixo Cache-Control: max-age Edge serve do cache até expirar
Revalidação ETag + If-None-Match Edge pergunta ao origin se mudou
Stale-while- stale-while-revalidate Serve stale, atualiza em background
revalidate
Purge manual API da CDN Operador invalida cache sob demanda
Cache busting URL versionada app.v2.js (nova URL = novo cache)
Exemplo de headers HTTP para cache de CDN:
Cache-Control: public, max-age=86400, stale-while-revalidate=3600
ETag: "abc123"
Vary: Accept-Encoding
Load Balancing
Load balancers distribuem tráfego entre múltiplas instâncias de um serviço. A escolha do algoritmo depende do tipo de aplicação e dos requisitos de consistência.
Algoritmo Descricao Uso
───────────────── ────────────────────────── ──────────────
Round-robin Alterna entre instancias Simples, uniforme
Least connections Envia para quem tem menos conn. Instancias heterogeneas
Weighted round-robin Round-robin com pesos Instancias diferentes
IP hash Mesmo IP sempre para mesma inst. Sessoes sticky
Consistent hashing Distribui por hash, minimo Caches distribuidos
reshuffling quando inst. muda
Tipos de load balancer por camada OSI:
L4 (Transporte): roteia por IP/porta (rápido, sem inspeção de conteúdo)
L7 (Aplicação): roteia por URL, headers, cookies (mais flexível)
import hashlib
class ConsistentHashRing:
"""Consistent hashing para distribuição de carga.
Minimiza redistribuição quando nodes são adicionados/removidos."""
def __init__(self, nodes, replicas=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, key):
return int(hashlib.md5(key.encode()).hexdigest(), 16)
def get_node(self, key):
"""Encontra o node responsável por esta chave."""
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]]
# Uso: distribuir requests entre 3 servidores
ring = ConsistentHashRing(["server-a", "server-b", "server-c"])
print(ring.get_node("user-123")) # server-b (sempre o mesmo)
print(ring.get_node("user-456")) # server-a
Service Mesh
Um service mesh é uma camada de infraestrutura dedicada que gerencia comunicação entre microserviços. O padrão mais comum é o sidecar proxy: cada serviço tem um proxy local que intercepta todo tráfego de rede.
Service Mesh (padrao sidecar):
┌──────────────────┐ ┌──────────────────┐
│ Service A │ │ Service B │
│ (sua aplicacao) │ │ (sua aplicacao) │
│ | │ │ | │
│ [Sidecar Proxy] │---mTLS---│ [Sidecar Proxy] │
│ (Envoy) │ │ (Envoy) │
└──────────────────┘ └──────────────────┘
O sidecar cuida de:
- mTLS automatico entre servicos
- Retries e timeouts
- Circuit breaking
- Observabilidade (metricas, traces)
- Load balancing
- Rate limiting
Exemplos: Istio (Kubernetes), Linkerd, Consul Connect
Arquitetura control plane vs data plane:
Data plane: Sidecar proxies (Envoy) — processa tráfego
Control plane: Istiod/Linkerd — configura proxies, distribui certs
Comunicação entre microserviços
Padroes de comunicacao entre microservicos:
1. Sincrono (request-response):
Service A --HTTP/gRPC--> Service B --> Response
Simples, mas cria acoplamento temporal
2. Assincrono (event-driven):
Service A --publish--> [Message Broker] --consume--> Service B
(RabbitMQ, Kafka, NATS)
Desacoplado, mas mais complexo
3. Saga pattern (transacoes distribuidas):
S1 --ok--> S2 --ok--> S3
S1 <--compensate-- S2 <--fail-- S3
(se um passo falha, compensa os anteriores)
Comparação de protocolos síncronos:
REST/HTTP: Texto (JSON), simples, amplamente suportado
gRPC: Binário (protobuf), tipado, streaming, mais eficiente
GraphQL: Query flexível, cliente pede exatamente o que precisa
Teorema CAP e consistência
O Teorema CAP (Brewer, 2000) afirma que um sistema distribuído só pode garantir duas das três propriedades simultaneamente:
Teorema CAP:
Consistency (C)
/\
/ \
/ \
/ CP \ CA
/________\
Partition Availability
tolerance(P) (A)
C (Consistência): Todos os nodes veem os mesmos dados ao mesmo tempo
A (Disponibilidade): Toda request recebe uma resposta (mesmo se dados stale)
P (Tolerância a partição): Sistema funciona mesmo com falhas de rede
Em redes reais, partições ACONTECEM → P é obrigatório
→ Escolha real: CP ou AP
CP (Consistência + Partição): PostgreSQL, ZooKeeper
Se há partição → recusa requests até sincronizar
AP (Disponibilidade + Partição): Cassandra, DynamoDB
Se há partição → serve dados possivelmente desatualizados
Modelos de consistência (do mais forte ao mais fraco):
Strong consistency: Leitura sempre retorna o valor mais recente
Linearizability: Operações parecem atômicas e ordenadas
Sequential consistency: Operações de cada cliente são ordenadas
Causal consistency: Respeita relações de causa-efeito
Eventual consistency: Eventualmente todos os nodes convergem
Replicação e consenso
Em sistemas distribuídos, dados são replicados entre nodes para tolerância a falhas. O problema central é: como os nodes concordam sobre o estado dos dados?
Algoritmo de consenso Raft (usado por etcd, Consul, CockroachDB):
1. Eleição de líder (leader election)
- Nodes começam como followers
- Se follower não recebe heartbeat → vira candidate
- Candidate pede votos → maioria → vira leader
2. Replicação de log
Leader Follower A Follower B
| | |
|---AppendEntries------>| |
|---AppendEntries------------------------------->|
| | |
|<--ACK-----------------| |
|<--ACK-----------------------------------------|
| | |
| (maioria confirmou → commit) |
|---Commit notify------>| |
|---Commit notify------------------------------->|
3. Se leader falha → nova eleição (timeout-based)
Quorum: N nodes, precisa de (N/2 + 1) para commit
3 nodes: quorum = 2 (tolera 1 falha)
5 nodes: quorum = 3 (tolera 2 falhas)
No harness.os
harness.os mesh = service mesh para agentes AI
Conceito de rede distribuída harness.os equivalente
────────────────────────── ──────────────────────────────
Microserviço Harness instance (marco.ai, build.ai)
Sidecar proxy MCP server (intercepta tool calls)
mTLS JWT auth entre nodes
Service discovery schema_reference + project table
Load balancing Agent orchestrator (concern routing)
Circuit breaker Token budget / error threshold
Observability claude_session_events table
CDN edge cache Prompt cache (tokens reutilizados)
Eventual consistency Nodes consultam DB sob demanda
Topologia do mesh harness.os:
┌─────────────┐
│ marco.ai │ (personal harness)
│ [MCP srv] │
└──────┬──────┘
│
┌────┴────┐
│ [Neon] │ (shared database = shared state)
│ purple │
│ cell │
└────┬────┘
│
┌──────┴──────┐
│ build.ai │ (build harness)
│ [MCP srv] │
└──────┬──────┘
│
┌──────┴──────┐
│ cortex.ai │ (product harness)
│ [MCP srv] │
└─────────────┘
Modelo de consistência do harness.os:
Hoje: Strong consistency via PostgreSQL (single writer, CP)
Trade-off: todas as reads/writes vão ao mesmo DB (gargalo potencial)
Evolução: eventual consistency com replicação local por node
Resumo
- CDNs reduzem latência servindo conteúdo de edge servers próximos via anycast routing
- Load balancers distribuem tráfego — consistent hashing minimiza redistribuição quando nodes mudam
- Service mesh: sidecar proxies gerenciam mTLS, retries, circuit breaking e observabilidade
- Comunicação síncrona (HTTP/gRPC) vs assíncrona (message broker) — trade-offs diferentes
- Teorema CAP: em sistemas distribuídos, escolha entre consistência e disponibilidade quando há partições
- Algoritmos de consenso (Raft) permitem que nodes concordem sobre estado em cenários de falha
Homework
Exercício 1: Projete uma estratégia de load balancing para o harness.os quando múltiplos agentes compartilham o mesmo MCP server.
- Qual algoritmo de load balancing é mais adequado? (round-robin? least connections? consistent hashing?)
- Como implementar rate limiting por agente?
- O que acontece quando o MCP server atinge capacidade máxima? (backpressure)
- Projete um circuit breaker: após N falhas, desabilite o agente por T segundos
Exercício 2: Implemente um consistent hash ring em Python que distribui tool calls entre 3 MCP server replicas. Teste adicionando e removendo um node — quantas chaves são redistribuídas?
Exercício 3: Classifique o harness.os no teorema CAP. Ele é CP ou AP? O que aconteceria se o Neon Postgres ficasse indisponível? Como o sistema poderia ser modificado para ser AP?
Exercício 4: Desenhe o fluxo completo de uma CDN servindo o site harness-os.pages.dev via Cloudflare. Inclua: DNS anycast, cache hit/miss, headers Cache-Control, e o que acontece quando você faz deploy de uma nova versão.
Verifique seu entendimento
Qual é a principal função do sidecar proxy em um service mesh?
Verifique seu entendimento
No Teorema CAP, por que sistemas distribuídos reais precisam escolher entre C e A?
Verifique seu entendimento
Em um cluster Raft com 5 nodes, qual é o número mínimo de nodes que precisam confirmar uma escrita para ela ser committed?