Service discovery e load balancing
1. Fundamentos de Service Discovery em Ambientes Distribuídos
1.1. Definição e importância
Em arquiteturas de microsserviços, os serviços precisam se comunicar entre si para processar requisições. Em um ambiente dinâmico, onde instâncias são criadas e destruídas constantemente (escalonamento horizontal, falhas, deploys), saber onde cada serviço está executando é um problema central. Service discovery é o mecanismo que permite localizar dinamicamente os endereços de rede de serviços disponíveis, sem depender de configurações estáticas.
A importância desse mecanismo é crítica: sem ele, cada serviço precisaria de uma lista fixa de endereços, que se tornaria rapidamente obsoleta em cenários de escalonamento ou falha.
1.2. Desafios da descoberta manual vs. automática
A descoberta manual (arquivos de configuração estáticos, variáveis de ambiente fixas) apresenta diversos problemas:
- Escalabilidade: a cada nova instância, é preciso atualizar todos os consumidores.
- Falhas: se uma instância cai, os consumidores continuam tentando se conectar a um endereço morto.
- Mudanças de endereço: em ambientes cloud, IPs e portas mudam frequentemente.
A descoberta automática resolve esses problemas mantendo um registro centralizado e atualizado de todas as instâncias saudáveis.
1.3. Modelos de descoberta
Client-side discovery: o cliente consulta um service registry (ex: Consul, Eureka) para obter a lista de endereços e aplica o balanceamento localmente. Exemplo:
GET /v1/health/service/api-gateway?passing=true
Resposta:
[
{"address": "10.0.1.5", "port": 8080},
{"address": "10.0.1.6", "port": 8080}
]
Server-side discovery: um balanceador de carga centralizado (ex: NGINX, AWS ELB) recebe a requisição e a encaminha para uma instância saudável. O cliente conhece apenas o endereço do balanceador.
2. Padrões de Implementação de Service Discovery
2.1. Registro e descoberta com service registry
Um service registry é um banco de dados distribuído que armazena o mapeamento entre nomes lógicos de serviços e seus endereços físicos. Exemplos populares:
- Consul: oferece DNS e API HTTP, com suporte a health checks customizados.
- Eureka: desenvolvido pela Netflix, foco em resiliência e disponibilidade (AP do teorema CAP).
- etcd: key-value store usado pelo Kubernetes, forte consistência (CP).
Fluxo de registro:
1. Serviço inicia → POST /v1/agent/service/register
Payload: {
"Name": "user-service",
"Address": "10.0.1.10",
"Port": 3000,
"Check": {"HTTP": "http://10.0.1.10:3000/health", "Interval": "10s"}
}
2. Registry armazena e começa a executar health checks periódicos.
2.2. Health checks e heartbeats
Health checks garantem que apenas instâncias saudáveis estejam no pool. Dois mecanismos comuns:
- Heartbeat: a instância envia periodicamente um sinal de vida (ex: TTL de 30s).
- Probe: o registry faz requisições HTTP, TCP ou executa scripts no endpoint de saúde.
Exemplo de heartbeat com Consul:
PUT /v1/agent/check/pass?check_id=service:user-service
Se o heartbeat falhar por N tentativas, a instância é marcada como "critical" e removida da descoberta.
2.3. Cache local e fallback
Para reduzir latência e evitar dependência total do registry, os clientes mantêm um cache local da lista de instâncias. Estratégias comuns:
- Cache com TTL: atualiza a cada 30-60 segundos.
- Cache com fallback: se o registry estiver indisponível, usa o último snapshot conhecido.
Exemplo de configuração com Spring Cloud Netflix:
eureka.client.cache-refresh-interval-ms: 30000
eureka.client.registry-fetch-interval-seconds: 30
3. Load Balancing: Conceitos e Estratégias
3.1. Balanceamento de carga no lado do cliente
Bibliotecas como Ribbon ou Spring Cloud LoadBalancer permitem que o cliente distribua as requisições entre as instâncias obtidas via service discovery.
Configuração de algoritmo round-robin:
user-service.ribbon.NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
Vantagem: elimina um ponto único de falha (balanceador central).
Desvantagem: complexidade no cliente.
3.2. Balanceamento de carga no lado do servidor
Balanceadores como NGINX e HAProxy atuam como intermediários. Exemplo de configuração NGINX:
upstream user-service {
server 10.0.1.10:3000 weight=3;
server 10.0.1.11:3000 weight=1;
server 10.0.1.12:3000 backup;
}
server {
listen 80;
location /users {
proxy_pass http://user-service;
}
}
Vantagem: centraliza a lógica de balanceamento e simplifica clientes.
Desvantagem: pode se tornar gargalo.
3.3. Algoritmos comuns
- Round-robin: distribui requisições sequencialmente entre instâncias.
- Least connections: envia para a instância com menos conexões ativas.
- Consistent hashing: garante que requisições do mesmo cliente sempre vão para a mesma instância (útil para cache).
- Weighted distribution: instâncias mais potentes recebem mais tráfego.
4. Integração entre Service Discovery e Load Balancing
4.1. Fluxo completo
1. Instância do user-service inicia e se registra no Consul.
2. Instância do api-gateway consulta Consul: "quais endereços do user-service estão saudáveis?"
3. Consul retorna: ["10.0.1.10:3000", "10.0.1.11:3000"]
4. api-gateway aplica load balancing (ex: round-robin) e seleciona 10.0.1.11:3000.
5. Requisição é encaminhada para a instância selecionada.
4.2. Sincronização de lista de endpoints
- Polling: o cliente consulta o registry em intervalos fixos (ex: 30s). Simples, mas pode ter latência na detecção de mudanças.
- Watch/Event-driven: o registry notifica os clientes sobre mudanças (ex: Consul watches, Kubernetes endpoints watchers). Menor latência, maior complexidade.
Exemplo de watch com Consul:
GET /v1/health/service/user-service?passing=true&watch=true
(conexão mantida aberta; retorna imediatamente e notifica em mudanças)
4.3. Tolerância a falhas
Combinar service discovery com padrões de resiliência:
- Retry: se uma requisição falha, tenta a próxima instância da lista.
- Failover: se o registry falha, usa cache local.
- Circuit breaker: se uma instância falha repetidamente, para de enviar tráfego para ela por um período.
5. Service Mesh como Abordagem Moderna
5.1. Sidecar proxy e descoberta transparente
Em um service mesh (ex: Istio com Envoy), um proxy sidecar é injetado em cada pod. O sidecar intercepta todo o tráfego de rede e gerencia service discovery e load balancing de forma transparente para a aplicação.
Pod A (user-service)
├── Container: user-service (aplicação)
└── Container: envoy (sidecar)
├── Descobre endpoints do Pod B via Istio Pilot
├── Aplica load balancing (maglev, round-robin)
└── Encaminha requisições com mTLS
5.2. Balanceamento avançado com mTLS
O service mesh permite balanceamento com criptografia automática (mTLS) e métricas detalhadas:
- mTLS: toda comunicação é criptografada e autenticada sem alteração no código.
- Métricas de tráfego: latência, taxa de erro, volume por serviço.
5.3. Comparação: service mesh vs. bibliotecas embarcadas
| Aspecto | Bibliotecas embarcadas | Service Mesh |
|---|---|---|
| Complexidade | Baixa (configuração na aplicação) | Alta (infraestrutura adicional) |
| Desempenho | Menor overhead | Overhead do proxy |
| Flexibilidade | Limitada à linguagem | Independente de linguagem |
| Observabilidade | Métricas da aplicação | Métricas de rede + aplicação |
6. Considerações de Projeto e Boas Práticas
6.1. Estratégias de deploy
Em deploys blue-green ou canary, o service discovery deve ser integrado:
Deploy canary:
1. Nova versão (v2) registra no registry com tag "version=v2".
2. Load balancer envia 10% do tráfego para instâncias com tag "v2".
3. Após validação, remove tag e envia 100%.
6.2. Observabilidade
Métricas essenciais:
- Tempo de registro: quanto tempo leva para uma instância ficar disponível após o start.
- Falhas de resolução: quantas vezes a descoberta falhou.
- Staleness do cache: idade média do cache local.
6.3. Segurança
- Autenticação no registry: tokens JWT ou mTLS para registrar e consultar.
- Criptografia: todas as comunicações entre serviços via TLS.
- Rate limiting: evitar que um cliente sobrecarregue o registry com consultas.
Referências
- Consul Documentation: Service Discovery — Documentação oficial do Consul explicando registro, health checks e consulta de serviços.
- Netflix Eureka Wiki — Wiki oficial do Eureka, com detalhes sobre registro, renovação e remoção de instâncias.
- NGINX Load Balancing Guide — Guia oficial de balanceamento de carga HTTP com NGINX, incluindo algoritmos e health checks.
- Envoy Proxy: Service Discovery — Documentação do Envoy sobre descoberta de serviços e integração com service mesh.
- Istio Service Mesh: Traffic Management — Conceitos de balanceamento de carga e descoberta no Istio, com exemplos de configuração de VirtualService e DestinationRule.
- Martin Fowler: Service Discovery — Artigo clássico sobre microsserviços, com seção dedicada a service discovery e seus desafios.
- HashiCorp Learn: Consul Service Discovery — Tutorial prático passo a passo para configurar service discovery com Consul.