College Online
0%

Redes Distribuidas

Modulo 5 · Aula 3 ~25 min de leitura Nivel: Avancado

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.

Diagrama
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?

Referência
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.

Referencia
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)
Python
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.

Diagrama
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

Diagrama
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:

Diagrama
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
i
PACELC — além do CAP O teorema PACELC (Abadi, 2012) refina o CAP: se há Partição, escolha entre Availability e Consistency; senão (Else), escolha entre Latency e Consistency. Sistemas como PostgreSQL são PC/EC (consistência forte sempre), enquanto DynamoDB é PA/EL (disponibilidade e baixa latência).

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?

Diagrama
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

Diagrama
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
*
Neon como "shared bus" No harness.os, o Neon Postgres funciona como um "barramento compartilhado" — todos os nodes leem e escrevem no mesmo banco. Isso simplifica a consistência (strong consistency via PostgreSQL), mas cria um ponto único de falha. Em uma evolução futura, cada node poderia ter um banco local com replicação eventual — similar à diferença entre CP e AP no teorema CAP.

Resumo

Homework

Exercício 1: Projete uma estratégia de load balancing para o harness.os quando múltiplos agentes compartilham o mesmo MCP server.

  1. Qual algoritmo de load balancing é mais adequado? (round-robin? least connections? consistent hashing?)
  2. Como implementar rate limiting por agente?
  3. O que acontece quando o MCP server atinge capacidade máxima? (backpressure)
  4. 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?

  • Armazenar dados do serviço localmente
  • Interceptar e gerenciar toda comunicação de rede do serviço (mTLS, retries, observability)
  • Compilar e deployar o serviço automaticamente
  • Gerenciar a base de dados do serviço

Verifique seu entendimento

No Teorema CAP, por que sistemas distribuídos reais precisam escolher entre C e A?

  • Porque partições de rede (P) são inevitáveis, então P é obrigatório — resta escolher entre C e A
  • Porque consistência e disponibilidade são sempre mutuamente exclusivas
  • Porque o teorema CAP é uma recomendação, não um limite teórico
  • Porque tolerância a partição não é possível em sistemas modernos

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?

  • 1 (apenas o leader)
  • 2 (leader + 1 follower)
  • 3 (maioria: 5/2 + 1 = 3)
  • 5 (todos os nodes)