Fundamentos7 min

Latência de rede: o custo invisível da comunicação

Em sistemas distribuídos, a rede é parte crítica da performance. Entenda como a latência de rede afeta seu sistema.

Em uma aplicação monolítica, chamadas de função são praticamente instantâneas — nanossegundos. Em sistemas distribuídos, cada chamada atravessa a rede, adicionando milissegundos de latência. Essa diferença de 6 ordens de magnitude muda tudo.

Este artigo explora como a latência de rede afeta performance e estratégias para minimizar seu impacto.

A rede é a mentira mais cara da computação distribuída.

O Custo Real da Rede

Comparação de latências

Operação Tempo típico
Acesso L1 cache 0.5 ns
Acesso RAM 100 ns
Leitura SSD 150 μs
Round-trip mesmo datacenter 0.5 ms
Round-trip mesma região 1-5 ms
Round-trip cross-region 50-150 ms
Round-trip intercontinental 100-300 ms

Uma chamada de rede no mesmo datacenter é 1 milhão de vezes mais lenta que acesso à RAM.

O problema se multiplica

Requisição do usuário
    ↓
API Gateway (1ms)
    ↓
Serviço A (2ms)
    ↓
Serviço B (2ms)
    ↓
Banco de dados (3ms)
    ↓
Resposta: 8ms apenas de rede

E isso assumindo que tudo dá certo na primeira tentativa.

Componentes da Latência de Rede

1. Propagação

Tempo para o sinal físico viajar pelo meio.

Velocidade da luz na fibra ≈ 200.000 km/s
São Paulo → Virginia ≈ 8.000 km
Tempo mínimo: 40ms (ida)
Round-trip: 80ms (mínimo teórico)

Você não pode vencer a física.

2. Transmissão

Tempo para colocar todos os bits no meio.

Payload 1MB em link de 1Gbps = 8ms
Payload 1MB em link de 100Mbps = 80ms

3. Processamento

Tempo em roteadores, firewalls, load balancers.

Cada hop adiciona microsegundos a milissegundos.

4. Enfileiramento

Espera quando links estão congestionados.

Pode variar de 0 a centenas de milissegundos.

Problemas Comuns

Chatty protocols

Muitas chamadas pequenas em vez de poucas chamadas grandes.

// Ruim: 100 chamadas de rede
for (id in ids) {
    items.push(await fetch(`/api/items/${id}`));
}

// Bom: 1 chamada de rede
items = await fetch(`/api/items?ids=${ids.join(',')}`);

N+1 em serviços

O mesmo problema de N+1 do banco, mas entre serviços.

// Serviço de pedidos
orders = getOrders(userId)         // 1 chamada
for order in orders:
    customer = getCustomer(order.customerId)  // N chamadas

Chamadas síncronas em cadeia

A → B → C → D → Banco
   5ms  5ms  5ms  5ms  = 20ms mínimo

Latência total é a soma de todas as chamadas.

Retry storms

Quando timeouts causam retries que causam mais carga que causa mais timeouts.

Serviço lento
    ↓
Timeout (2s)
    ↓
3 retries × 1000 clientes = 3000 requisições extras
    ↓
Serviço ainda mais lento
    ↓
Colapso

Estratégias de Mitigação

1. Reduza chamadas

Batching: agrupe operações

// Em vez de
await Promise.all(ids.map(id => getItem(id)));

// Use batch API
await getItems(ids);

Prefetching: busque dados antes de precisar

// Enquanto processa página 1, busque página 2
const page1 = await getPage(1);
const page2Promise = getPage(2);  // Já iniciou
// ... processa página 1 ...
const page2 = await page2Promise;

2. Paralelismo

// Sequencial: 15ms
const a = await serviceA();  // 5ms
const b = await serviceB();  // 5ms
const c = await serviceC();  // 5ms

// Paralelo: 5ms
const [a, b, c] = await Promise.all([
    serviceA(),
    serviceB(),
    serviceC()
]);

3. Cache agressivo

Evite chamadas de rede quando possível.

const cache = new Map();

async function getUser(id) {
    if (cache.has(id)) return cache.get(id);

    const user = await userService.get(id);
    cache.set(id, user);
    return user;
}

4. Compressão

Reduza bytes transmitidos.

Resposta JSON: 100KB
Comprimido (gzip): 15KB
Economia: 85% de bandwidth

5. Keep-alive connections

Evite overhead de estabelecer conexões.

Nova conexão TCP: ~1-3ms
Conexão existente: ~0ms overhead

6. Localidade

Mantenha serviços que se comunicam muito próximos.

Serviço A e B na mesma zona: 0.5ms
Serviço A e B em regiões diferentes: 50ms

7. Comunicação assíncrona

Quando possível, não espere resposta.

// Síncrono: bloqueia
await notificationService.send(email);

// Assíncrono: não bloqueia
messageQueue.publish('send-email', email);

Timeouts e Retries

Configurando timeouts

Timeout = p99 latência × 2 (ou mais)

Muito curto: falsos positivos Muito longo: recursos presos

Retry com backoff

async function fetchWithRetry(url, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await fetch(url);
        } catch (e) {
            if (i === maxRetries - 1) throw e;
            await sleep(Math.pow(2, i) * 100);  // Exponential backoff
        }
    }
}

Circuit breaker

Pare de tentar quando o serviço está claramente com problemas.

if (failureRate > 50%) {
    // Circuito aberto: falhe rápido
    throw new Error('Service unavailable');
}

Métricas Essenciais

Métrica Por que importa
Latência p50, p95, p99 Experiência real do usuário
Request rate Volume de chamadas
Error rate Falhas de comunicação
Retries Indica problemas de estabilidade
Connection pool usage Pressão de conexões
Bytes in/out Volume de dados

Conclusão

A rede é uma realidade inevitável em sistemas distribuídos. Para minimizar seu impacto:

  1. Reduza chamadas — batch, cache, prefetch
  2. Paraleleze — quando não há dependência
  3. Comprima — menos bytes = menos tempo
  4. Planeje localidade — serviços próximos = menor latência
  5. Use async — não espere quando não precisa
  6. Configure timeouts — não espere para sempre
  7. Monitore — você não pode melhorar o que não mede

Cada chamada de rede é uma aposta. Minimize suas apostas.

redelatênciadistribuídosmicroserviços
Compartilhar:
Read in English

Quer entender os limites da sua plataforma?

Entre em contato para uma avaliação de performance.

Fale Conosco