Voltar ao blog

Proxies para Arquitetura de Microsserviços: Proteção de API, Balanceamento e Segurança

Guia completo para integração de proxy na arquitetura de microsserviços: proteção de API, balanceamento de carga, segurança na comunicação entre serviços e exemplos de configuração.

📅18 de fevereiro de 2026
```html

A arquitetura de microserviços requer comunicação confiável entre serviços, proteção de solicitações de API externas e balanceamento de carga. Os servidores proxy resolvem essas questões, atuando como intermediários entre serviços, APIs externas e clientes. Neste guia, vamos explorar como integrar corretamente um proxy na infraestrutura de microserviços, quais tipos de proxy usar para diferentes cenários e como configurar uma comunicação segura.

O papel do proxy na arquitetura de microserviços

Na arquitetura de microserviços, os servidores proxy desempenham várias funções críticas que diferem do uso tradicional de proxy para anonimização ou contorno de bloqueios. Aqui, os proxies se tornam uma parte integrante da infraestrutura, garantindo comunicação confiável e segura entre os componentes do sistema.

Principais funções dos proxies em microserviços:

  • API Gateway — um único ponto de entrada para todas as solicitações dos clientes, que as roteia para os microserviços correspondentes, ocultando a arquitetura interna do sistema
  • Sidecar Proxy — um proxy-container que opera ao lado de cada serviço (padrão Service Mesh), interceptando todo o tráfego de entrada e saída
  • Reverse Proxy — distribuição de carga entre várias instâncias de um serviço, garantindo resiliência
  • Forward Proxy — controle e proteção de solicitações de saída para APIs externas, ocultando os endereços IP internos da infraestrutura
  • Proxy de segurança — terminação SSL/TLS, autenticação, autorização, proteção contra DDoS e outros ataques

Os proxies permitem implementar padrões arquiteturais importantes: circuit breaker (desconexão automática de serviços inoperantes), retry logic (tentativas de repetição em caso de falhas), rate limiting (limitação de frequência de solicitações), request/response transformation (transformação de formatos de dados). Tudo isso torna o sistema mais resistente a falhas e simplifica a gestão de uma infraestrutura distribuída complexa.

Importante: Na arquitetura de microserviços, os proxies operam em dois níveis — como gateway externo para clientes (API Gateway) e como proxies internos entre serviços (Service Mesh). Ambos os níveis são críticos para a segurança e confiabilidade do sistema.

Tipos de proxy para diferentes cenários de uso

A escolha do tipo de proxy depende da tarefa específica na arquitetura de microserviços. Diferentes cenários exigem diferentes características: velocidade, confiabilidade, anonimato ou distribuição geográfica.

Cenário Tipo de proxy Por que
Comunicação interna entre serviços Proxy HTTP/HTTPS (Envoy, NGINX) Máxima velocidade, baixa latência, suporte a HTTP/2
Solicitações a APIs externas com limites Proxies residenciais Contornar limites de taxa, IPs reais de usuários, baixo risco de bloqueios
Extração de dados para análise Proxies de data center Alta velocidade, baixo custo, adequado para solicitações em massa
Trabalho com APIs móveis Proxies móveis Simulação de usuários móveis reais, acesso a APIs exclusivas para dispositivos móveis
Balanceamento de carga Reverse Proxy (HAProxy, NGINX) Distribuição de tráfego, verificações de saúde, troca automática em caso de falhas
Sistema geograficamente distribuído Proxies residenciais com geo-targeting Acesso a APIs regionais, conformidade com requisitos de localização de dados

Para comunicação interna entre microserviços, geralmente são usadas soluções de proxy especializadas como Envoy Proxy ou NGINX, que são otimizadas para baixa latência e alta capacidade. Elas suportam protocolos modernos (HTTP/2, gRPC) e se integram com sistemas Service Mesh.

Para trabalhar com APIs externas, a escolha depende das exigências do serviço específico. Se a API tiver limites de taxa rigorosos ou bloquear solicitações de IPs de data center, proxies residenciais são necessários. Para coleta de dados em massa, onde a velocidade é mais importante que o anonimato, proxies de data center são adequados. Proxies móveis são necessários ao trabalhar com APIs que verificam o tipo de dispositivo ou exigem endereços IP móveis.

Proxy como API Gateway: proteção e roteamento

O API Gateway é um servidor proxy especializado que atua como um único ponto de entrada para todas as solicitações de clientes ao sistema de microserviços. Em vez de os clientes se conectarem diretamente a dezenas de serviços diferentes, eles enviam todas as solicitações para um único endereço do API Gateway, que as roteia para os serviços necessários.

Principais funções do API Gateway:

  • Roteamento de solicitações — determinação de qual microserviço deve processar a solicitação com base na URL, cabeçalhos ou outros parâmetros
  • Autenticação e autorização — verificação de tokens (JWT, OAuth), gerenciamento de acesso a diferentes serviços
  • Rate Limiting — limitação do número de solicitações de um único cliente para proteção contra sobrecarga e DDoS
  • Agregação de respostas — combinação de dados de vários serviços em uma única resposta ao cliente
  • Transformação de protocolos — conversão de REST para gRPC, HTTP/1.1 para HTTP/2
  • Cache — armazenamento de dados frequentemente solicitados para reduzir a carga sobre os serviços
  • Registro e monitoramento — coleta centralizada de métricas e logs de todas as solicitações

Soluções populares para API Gateway: Kong, Tyk, AWS API Gateway, Azure API Management, NGINX Plus, Traefik. A escolha depende da escala do sistema, dos requisitos de desempenho e da plataforma de nuvem utilizada.

// Exemplo de configuração do NGINX como API Gateway
upstream auth_service {
    server auth:8001;
}

upstream user_service {
    server user:8002;
}

upstream order_service {
    server order:8003;
}

server {
    listen 80;
    server_name api.example.com;

    # Limitação de taxa de solicitações
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    location /api/auth/ {
        limit_req zone=api_limit burst=20;
        proxy_pass http://auth_service/;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /api/users/ {
        # Verificação do token antes do proxy
        auth_request /auth/verify;
        proxy_pass http://user_service/;
    }

    location /api/orders/ {
        auth_request /auth/verify;
        proxy_pass http://order_service/;
    }

    # Endpoint interno para verificação de tokens
    location = /auth/verify {
        internal;
        proxy_pass http://auth_service/verify;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
    }
}

O API Gateway oculta a arquitetura interna do sistema dos clientes externos. Os clientes não sabem quantos microserviços existem e como eles interagem — eles veem apenas uma API única. Isso simplifica o versionamento, permite alterar a estrutura interna sem impactar os clientes e melhora a segurança, pois os serviços internos não estão acessíveis diretamente da internet.

Integração com Service Mesh (Istio, Linkerd)

O Service Mesh é uma camada de infraestrutura que gerencia a comunicação entre microserviços por meio de servidores proxy implantados ao lado de cada serviço (padrão Sidecar). Ao contrário do API Gateway, que lida apenas com o tráfego externo, o Service Mesh controla todo o tráfego interno entre os serviços.

As soluções mais populares de Service Mesh são Istio (que usa o Envoy Proxy como sidecar) e Linkerd (que usa seu próprio proxy leve). Elas implantam automaticamente um container proxy ao lado de cada pod no Kubernetes, interceptando todo o tráfego de entrada e saída.

Funcionalidades do Service Mesh através de proxies:

  • Mutual TLS (mTLS) — criptografia automática de todo o tráfego entre serviços com autenticação mútua
  • Gerenciamento de Tráfego — controle de roteamento, implantações canárias, testes A/B
  • Observabilidade — coleta automática de métricas, rastreamentos e logs sem modificar o código dos serviços
  • Resiliência — circuit breaking, retry logic, gerenciamento de timeouts, injeção de falhas para testes
  • Descoberta de Serviços — descoberta automática de serviços e balanceamento de carga
# Exemplo de configuração do Istio VirtualService para roteamento
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: user-service
spec:
  hosts:
  - user-service
  http:
  - match:
    - headers:
        version:
          exact: "v2"
    route:
    - destination:
        host: user-service
        subset: v2
  - route:
    - destination:
        host: user-service
        subset: v1
      weight: 90
    - destination:
        host: user-service
        subset: v2
      weight: 10  # Implantação canária: 10% do tráfego para v2

---
# Circuit Breaker para proteção contra falhas em cascata
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: user-service
spec:
  host: user-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 2
    outlierDetection:
      consecutiveErrors: 5
      interval: 30s
      baseEjectionTime: 30s
      maxEjectionPercent: 50

O Service Mesh resolve o problema do "monolito distribuído" — quando a lógica de interação entre serviços (retry, timeout, circuit breaking) é duplicada no código de cada serviço. Em vez disso, toda essa lógica é movida para a camada de proxy, simplificando o código dos serviços e garantindo um comportamento uniforme em todo o sistema.

Uma vantagem importante é a transparência total do tráfego. Cada solicitação entre serviços passa por um proxy que registra métricas: tempo de resposta, códigos de erro, tamanho do payload. Esses dados são enviados automaticamente para sistemas de monitoramento (Prometheus, Grafana) e rastreamento (Jaeger, Zipkin), criando uma visão completa do funcionamento do sistema distribuído sem a necessidade de adicionar instrumentação ao código de cada serviço.

Proteção de solicitações a APIs externas através de proxy

Microserviços frequentemente interagem com APIs externas: sistemas de pagamento, serviços de geolocalização, APIs de redes sociais, fornecedores de dados. Solicitações diretas a APIs externas criam vários problemas: exposição de endereços IP internos da infraestrutura, risco de bloqueio ao exceder limites de taxa, falta de controle sobre o tráfego de saída.

O uso de proxies para solicitações de saída resolve esses problemas e adiciona funcionalidades adicionais:

  • Ocultação da infraestrutura — APIs externas veem endereços IP dos proxies, não dos seus servidores
  • Contorno de limites de taxa — rotação de endereços IP para distribuição de solicitações
  • Distribuição geográfica — acesso a APIs regionais através de proxies nos países necessários
  • Gerenciamento centralizado — um único ponto de controle para todas as solicitações de saída
  • Cache de respostas — redução do número de solicitações a APIs caras
  • Monitoramento e registro — rastreamento de todas as interações com serviços externos
// Python: configuração de proxy para requests a APIs externas
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class ExternalAPIClient:
    def __init__(self, proxy_url, proxy_rotation=False):
        self.session = requests.Session()
        
        # Configuração do proxy
        self.proxies = {
            'http': proxy_url,
            'https': proxy_url
        }
        
        # Lógica de retry para resiliência
        retry_strategy = Retry(
            total=3,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["GET", "POST"]
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)
    
    def call_payment_api(self, data):
        """Solicitação ao API de pagamento através do proxy"""
        try:
            response = self.session.post(
                'https://api.payment-provider.com/charge',
                json=data,
                proxies=self.proxies,
                timeout=10,
                headers={'User-Agent': 'MyService/1.0'}
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            # Registro do erro
            print(f"Erro na API de pagamento: {e}")
            raise

# Uso com um pool de proxies para rotação
class ProxyPool:
    def __init__(self, proxy_list):
        self.proxies = proxy_list
        self.current = 0
    
    def get_next(self):
        proxy = self.proxies[self.current]
        self.current = (self.current + 1) % len(self.proxies)
        return proxy

# Inicialização
proxy_pool = ProxyPool([
    'http://user:pass@proxy1.example.com:8080',
    'http://user:pass@proxy2.example.com:8080',
    'http://user:pass@proxy3.example.com:8080'
])

# Para cada solicitação, usamos o próximo proxy
client = ExternalAPIClient(proxy_pool.get_next())

Para trabalhar com APIs externas que têm restrições rigorosas ou bloqueiam solicitações de IPs de data center, proxies residenciais se tornam uma necessidade. Eles fornecem endereços IP reais de usuários domésticos, o que reduz o risco de bloqueio e permite contornar restrições geográficas.

// Node.js: proxy para APIs externas com rotação automática
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');

class ExternalAPIService {
  constructor(proxyList) {
    this.proxyList = proxyList;
    this.currentProxyIndex = 0;
    this.requestCounts = new Map(); // Contador de solicitações para limitação de taxa
  }

  getNextProxy() {
    const proxy = this.proxyList[this.currentProxyIndex];
    this.currentProxyIndex = (this.currentProxyIndex + 1) % this.proxyList.length;
    return proxy;
  }

  async callAPI(endpoint, data, options = {}) {
    const proxyUrl = this.getNextProxy();
    const agent = new HttpsProxyAgent(proxyUrl);

    // Limitação de taxa: não mais que 100 solicitações por minuto por proxy
    const proxyKey = proxyUrl;
    const now = Date.now();
    const count = this.requestCounts.get(proxyKey) || { count: 0, resetTime: now + 60000 };
    
    if (count.count >= 100 && now < count.resetTime) {
      // Mudar para o próximo proxy
      return this.callAPI(endpoint, data, options);
    }

    try {
      const response = await axios({
        method: options.method || 'POST',
        url: endpoint,
        data: data,
        httpsAgent: agent,
        timeout: options.timeout || 10000,
        headers: {
          'User-Agent': 'Mozilla/5.0 (compatible; MyService/1.0)',
          ...options.headers
        }
      });

      // Atualizar contador
      if (now >= count.resetTime) {
        this.requestCounts.set(proxyKey, { count: 1, resetTime: now + 60000 });
      } else {
        count.count++;
      }

      return response.data;
    } catch (error) {
      if (error.response?.status === 429) {
        // Limite de taxa excedido - mudar para outro proxy
        console.log(`Limite de taxa em ${proxyUrl}, mudando proxy`);
        return this.callAPI(endpoint, data, options);
      }
      throw error;
    }
  }
}

// Uso
const apiService = new ExternalAPIService([
  'http://user:pass@proxy1.example.com:8080',
  'http://user:pass@proxy2.example.com:8080'
]);

module.exports = apiService;

Balanceamento de carga e resiliência

Os servidores proxy desempenham um papel crucial na garantia de alta disponibilidade do sistema de microserviços através do balanceamento de carga e da troca automática em caso de falhas. Quando você tem várias instâncias de um serviço (para escalabilidade horizontal), o proxy distribui as solicitações entre elas, garantindo uma carga uniforme.

Principais algoritmos de balanceamento de carga:

  • Round Robin — envio sequencial de solicitações para cada servidor na lista, simples e eficaz para servidores homogêneos
  • Least Connections — envio da solicitação para o servidor com o menor número de conexões ativas, adequado para solicitações longas
  • IP Hash — vinculação do cliente a um servidor específico com base em seu IP, garantindo sessões persistentes
  • Weighted Round Robin — distribuição levando em conta a capacidade dos servidores (servidores mais potentes recebem mais solicitações)
  • Random — escolha aleatória de servidor, adequado para serviços sem estado
# Configuração do HAProxy para balanceamento com verificações de saúde
global
    maxconn 4096
    log stdout format raw local0

defaults
    mode http
    timeout connect 5s
    timeout client 50s
    timeout server 50s
    option httplog

frontend api_frontend
    bind *:80
    default_backend api_servers

backend api_servers
    balance roundrobin
    
    # Verificação de saúde: checagem em /health a cada 2 segundos
    option httpchk GET /health
    http-check expect status 200
    
    # Lógica de retry
    retries 3
    option redispatch
    
    # Servidores com pesos (server3 é 2 vezes mais potente)
    server server1 10.0.1.10:8080 check weight 1 maxconn 500
    server server2 10.0.1.11:8080 check weight 1 maxconn 500
    server server3 10.0.1.12:8080 check weight 2 maxconn 1000
    
    # Servidor de backup (usado apenas se os principais estiverem indisponíveis)
    server backup1 10.0.2.10:8080 check backup

As verificações de saúde são uma função crítica para a resiliência. O proxy verifica regularmente a disponibilidade de cada servidor (geralmente através do endpoint HTTP /health ou /ready) e exclui automaticamente servidores não operacionais do pool de balanceamento. Quando um servidor se recupera e começa a responder às verificações de saúde, ele retorna automaticamente ao pool.

Estratégias de resiliência através de proxies:

  • Active Health Checks — o proxy interroga ativamente os servidores para verificar sua disponibilidade
  • Passive Health Checks — o proxy monitora solicitações reais e exclui servidores ao acumular erros
  • Circuit Breaker — desligamento temporário de um serviço problemático para evitar falhas em cascata
  • Graceful Degradation — mudança para um modo de operação simplificado ou dados em cache em caso de falhas
  • Failover to Backup — troca automática para servidores ou regiões de backup
// Python: implementação de Circuit Breaker para proxy a serviços externos
from datetime import datetime, timedelta
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"      # Funcionamento normal
    OPEN = "open"          # Serviço indisponível, solicitações bloqueadas
    HALF_OPEN = "half_open"  # Modo de teste após recuperação

class CircuitBreaker:
    def __init__(self, failure_threshold=5, timeout=60, success_threshold=2):
        self.failure_threshold = failure_threshold  # Erros até abrir
        self.timeout = timeout  # Segundos até a tentativa de recuperação
        self.success_threshold = success_threshold  # Sucessos para fechar
        
        self.state = CircuitState.CLOSED
        self.failures = 0
        self.successes = 0
        self.last_failure_time = None
    
    def call(self, func, *args, **kwargs):
        if self.state == CircuitState.OPEN:
            if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
                self.state = CircuitState.HALF_OPEN
                print("Circuit breaker: transição para HALF_OPEN")
            else:
                raise Exception("Circuit breaker OPEN: serviço indisponível")
        
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise e
    
    def _on_success(self):
        self.failures = 0
        if self.state == CircuitState.HALF_OPEN:
            self.successes += 1
            if self.successes >= self.success_threshold:
                self.state = CircuitState.CLOSED
                self.successes = 0
                print("Circuit breaker: recuperação, transição para CLOSED")
    
    def _on_failure(self):
        self.failures += 1
        self.last_failure_time = datetime.now()
        if self.failures >= self.failure_threshold:
            self.state = CircuitState.OPEN
            print(f"Circuit breaker OPEN: {self.failures} erros consecutivos")

# Uso
breaker = CircuitBreaker(failure_threshold=3, timeout=30)

def call_external_service():
    # Seu código de solicitação a API externa através do proxy
    pass

try:
    result = breaker.call(call_external_service)
except Exception as e:
    # Lógica de fallback: cache, valores padrão, etc.
    print(f"Serviço indisponível: {e}")

Segurança na comunicação entre serviços

Na arquitetura de microserviços, os servidores proxy oferecem vários níveis de segurança: criptografia de tráfego, autenticação de serviços, proteção contra ataques e isolamento de segmentos de rede. Sem a configuração adequada de segurança, o tráfego interno entre serviços pode ser interceptado ou falsificado.

Aspectos-chave de segurança através de proxies:

  • Mutual TLS (mTLS) — autenticação bidirecional, onde tanto o cliente quanto o servidor verificam os certificados um do outro. O Service Mesh configura automaticamente o mTLS entre todos os serviços
  • TLS Termination — o proxy descriptografa o tráfego HTTPS na borda, verifica e o transmite para os serviços por um canal seguro
  • JWT Validation — verificação de tokens de acesso no nível do proxy, antes que a solicitação chegue ao serviço
  • IP Whitelisting — restrição de acesso a serviços apenas a partir de endereços IP permitidos
  • DDoS Protection — limitação de taxa, limites de conexão, proteção contra SYN flood no nível do proxy
  • WAF (Web Application Firewall) — filtragem de solicitações maliciosas, proteção contra SQL injection, XSS
# Configuração do NGINX com SSL/TLS e segurança
server {
    listen 443 ssl http2;
    server_name api.internal.example.com;

    # Certificados SSL
    ssl_certificate /etc/nginx/certs/api.crt;
    ssl_certificate_key /etc/nginx/certs/api.key;
    
    # Protocolos e cifras modernas
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # Certificado do cliente para mTLS
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client on;
    
    # Cabeçalhos seguros
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # Limitação de taxa
    limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
    limit_req zone=api burst=200 nodelay;
    
    # Limitação de conexão
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn addr 10;
    
    # Whitelisting de IP
    allow 10.0.0.0/8;      # Rede interna
    allow 172.16.0.0/12;   # VPC
    deny all;
    
    location / {
        # Verificação do token JWT
        auth_jwt "API Restrita";
        auth_jwt_key_file /etc/nginx/jwt_key.json;
        
        proxy_pass http://backend_service;
        
        # Transmissão de informações sobre o certificado do cliente
        proxy_set_header X-Client-DN $ssl_client_s_dn;
        proxy_set_header X-Client-Verify $ssl_client_verify;
    }
}

O Service Mesh simplifica significativamente a configuração de segurança, gerando e rotacionando automaticamente certificados para mTLS, aplicando políticas de acesso e criptografando todo o tráfego entre serviços. Por exemplo, no Istio, é possível definir uma política que o serviço "payment" pode aceitar solicitações apenas do serviço "order", e isso será aplicado automaticamente no nível do proxy sem modificar o código dos serviços.

Importante para produção: Sempre use mTLS para comunicação interna entre serviços, mesmo que estejam na mesma rede privada. Isso protege contra ataques do tipo man-in-the-middle e garante autenticação em nível de serviço, e não apenas em nível de rede.

Monitoramento e registro de tráfego de proxy

Os servidores proxy oferecem uma oportunidade única para monitoramento centralizado de todo o tráfego no sistema de microserviços. Como todo o tráfego passa pelo proxy (tanto externo através do API Gateway quanto interno através do Service Mesh), você obtém visibilidade completa do funcionamento do sistema sem a necessidade de instrumentar cada serviço.

Métricas-chave para monitoramento no nível do proxy:

  • Latência — tempo de processamento da solicitação em cada etapa: proxy, serviço, APIs externas
  • Throughput — número de solicitações por segundo, volume de dados transmitidos
  • Taxa de Erros — porcentagem de erros (4xx, 5xx), tipos de erros, endpoints problemáticos
  • Métricas de Conexão — número de conexões ativas, uso do pool de conexões
  • Estado do Circuit Breaker — estado dos circuit breakers para cada serviço
  • Métricas SSL/TLS — status dos certificados, versões dos protocolos, erros de handshake
# Configuração do NGINX para exportar métricas para Prometheus
server {
    listen 9113;
    location /metrics {
        stub_status;
        access_log off;
        allow 10.0.0.0/8;  # Apenas para o servidor Prometheus
        deny all;
    }
}

# Registro em formato JSON para logging estruturado
log_format json_combined escape=json
  '{'
    '"time_local":"$time_local",'
    '"remote_addr":"$remote_addr",'
    '"request":"$request",'
    '"status": "$status",'
    '"body_bytes_sent":"$body_bytes_sent",'
    '"request_time":"$request_time",'
    '"upstream_response_time":"$upstream_response_time",'
    '"upstream_addr":"$upstream_addr",'
    '"http_referrer":"$http_referer",'
    '"http_user_agent":"$http_user_agent",'
    '"http_x_forwarded_for":"$http_x_forwarded_for"'
  '}';

access_log /var/log/nginx/access.log json_combined;

O rastreamento distribuído é uma das funcionalidades mais poderosas de monitoramento através de proxies. Cada solicitação recebe um ID de rastreamento único, que o proxy adiciona aos cabeçalhos e transmite adiante pela cadeia de serviços. Sistemas de rastreamento (Jaeger, Zipkin) coletam informações de todos os proxies e constroem o caminho completo da solicitação através do sistema, mostrando quanto tempo ela passou em cada serviço.

// Node.js: adicionando rastreamento no middleware de proxy
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');

const app = express();

// Middleware para adicionar ID de rastreamento
app.use((req, res, next) => {
  // Obtém o ID de rastreamento do cabeçalho ou cria um novo
  const traceId = req.headers['x-trace-id'] || uuidv4();
  const spanId = uuidv4();
  
  // Adiciona aos cabeçalhos para transmissão
  req.traceId = traceId;
  req.spanId = spanId;
  res.setHeader('x-trace-id', traceId);
  
  // Registra o início do processamento
  const startTime = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - startTime;
    
    // Log estruturado para análise
    console.log(JSON.stringify({
      timestamp: new Date().toISOString(),
      traceId: traceId,
      spanId: spanId,
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration: duration,
      userAgent: req.headers['user-agent'],
      ip: req.ip
    }));
  });
  
  next();
});

// Endpoint de proxy com transmissão de cabeçalhos de rastreamento
app.all('/api/*', async (req, res) => {
  const targetService = determineTargetService(req.path);
  
  try {
    const response = await axios({
      method: req.method,
      url: `http://${targetService}${req.path}`,
      data: req.body,
      headers: {
        ...req.headers,
        'x-trace-id': req.traceId,
        'x-parent-span-id': req.spanId,
        'x-span-id': uuidv4()  // Novo span para solicitação downstream
      }
    });
    
    res.status(response.status).json(response.data);
  } catch (error) {
    console.error(JSON.stringify({
      traceId: req.traceId,
      error: error.message,
      service: targetService
    }));
    res.status(500).json({ error: 'Serviço indisponível' });
  }
});

function determineTargetService(path) {
  if (path.startsWith('/api/users')) return 'user-service:8080';
  if (path.startsWith('/api/orders')) return 'order-service:8080';
  return 'default-service:8080';
}

app.listen(3000);

Alertas baseados em métricas de proxy permitem detectar problemas rapidamente. Por exemplo, é possível configurar alertas para: aumento repentino da latência (possivelmente, um dos serviços está degradando), aumento da taxa de erros acima do limite (problemas com código ou dependências), alteração nos padrões de tráfego (possível ataque DDoS ou carga viral).

Exemplos de implementação em Python e Node.js

Vamos considerar exemplos práticos de integração de proxy em microserviços em Python e Node.js para diferentes cenários: comunicação interna, trabalho com APIs externas, balanceamento de carga.

Python: serviço com proxy para APIs externas

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import httpx
import asyncio
from typing import List, Optional
import logging

app = FastAPI()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ProxyConfig(BaseModel):
    url: str
    max_requests_per_minute: int = 60

class ProxyPool:
    def __init__(self, proxies: List[ProxyConfig]):
        self.proxies = proxies
        self.current_index = 0
        self.request_counts = {p.url: 0 for p in proxies}
        self.reset_time = asyncio.get_event_loop().time() + 60
    
    async def get_next_proxy(self) -> str:
        # Reiniciar contadores a cada minuto
        current_time = asyncio.get_event_loop().time()
        if current_time >= self.reset_time:
            self.request_counts = {p.url: 0 for p in self.proxies}
            self.reset_time = current_time + 60
        
        # Encontrar proxy com solicitações disponíveis
        for _ in range(len(self.proxies)):
            proxy = self.proxies[self.current_index]
            self.current_index = (self.current_index + 1) % len(self.proxies)
            
            if self.request_counts[proxy.url] < proxy.max_requests_per_minute:
                self.request_counts[proxy.url] += 1
                return proxy.url
        
        # Todos os proxies esgotaram o limite
        raise HTTPException(status_code=429, detail="Todos os proxies com limite de taxa")

# Inicialização do pool de proxies
proxy_pool = ProxyPool([
    ProxyConfig(url="http://user:pass@proxy1.example.com:8080", max_requests_per_minute=100),
    ProxyConfig(url="http://user:pass@proxy2.example.com:8080", max_requests_per_minute=100),
    ProxyConfig(url="http://user:pass@proxy3.example.com:8080", max_requests_per_minute=100)
])

class ExternalAPIClient:
    def __init__(self, proxy_pool: ProxyPool):
        self.proxy_pool = proxy_pool
    
    async def fetch_data(self, endpoint: str, params: dict = None) -> dict:
        proxy_url = await self.proxy_pool.get_next_proxy()
        
        async with httpx.AsyncClient(proxies={"http://": proxy_url, "https://": proxy_url}) as client:
            try:
                response = await client.get(
                    endpoint,
                    params=params,
                    timeout=10.0,
                    headers={"User-Agent": "MyMicroservice/1.0"}
                )
                response.raise_for_status()
                
                logger.info(f"Dados obtidos com sucesso de {endpoint} via {proxy_url}")
                return response.json()
            
            except httpx.HTTPStatusError as e:
                logger.error(f"Erro HTTP {e.response.status_code} de {endpoint}")
                raise HTTPException(status_code=e.response.status_code, detail=str(e))
            
            except httpx.RequestError as e:
                logger.error(f"Erro de solicitação para {endpoint}: {e}")
                raise HTTPException(status_code=503, detail="API externa indisponível")

api_client = ExternalAPIClient(proxy_pool)

@app.get("/data/{resource_id}")
async def get_external_data(resource_id: str):
    """Endpoint que obtém dados de uma API externa através de um proxy"""
    external_endpoint = f"https://api.external-service.com/v1/resources/{resource_id}"
    
    try:
        data = await api_client.fetch_data(external_endpoint)
        return {"status": "success", "data": data}
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Erro inesperado: {e}")
        raise HTTPException(status_code=500, detail="Erro interno do servidor")

@app.get("/health")
async def health_check():
    return {"status": "healthy", "service": "external-api-proxy"}

# Execução: uvicorn main:app --host 0.0.0.0 --port 8000

Node.js: API Gateway com balanceamento de carga

const express = require('express');
const axios = require('axios');
const rateLimit = require('express-rate-limit');

const app = express();
app.use(express.json());

// Configuração dos microserviços
const services = {
  users: [
    { url: 'http://user-service-1:8001', healthy: true, activeConnections: 0 },
    { url: 'http://user-service-2:8001', healthy: true, activeConnections: 0 },
    // Adicione mais serviços conforme necessário
  ],
  // Adicione outros serviços aqui
};

// Lógica de balanceamento de carga
app.use('/api/users', rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minuto
  max: 100 // Limite de 100 solicitações por minuto
}));

app.get('/api/users', async (req, res) => {
  const service = services.users.find(s => s.healthy);
  if (!service) {
    return res.status(503).json({ error: 'Todos os serviços estão indisponíveis' });
  }

  try {
    const response = await axios.get(`${service.url}/users`);
    res.json(response.data);
  } catch (error) {
    console.error(`Erro ao chamar ${service.url}: ${error.message}`);
    res.status(500).json({ error: 'Erro ao chamar serviço' });
  }
});

// Iniciar o servidor
app.listen(3000, () => {
  console.log('API Gateway rodando na porta 3000');
});
```