A Armadilha do Excesso de Código Customizado em Gateways
Muitas equipes de desenvolvimento, ao implementar um API Gateway, cometem o erro de criar filtros customizados para funcionalidades que já existem nativamente no framework. Esse hábito transforma problemas de configuração em problemas de manutenção de código, aumentendo a complexidade técnica e o débito técnico do projeto.
O Spring Cloud Gateway oferece mais de 30 fábricas de filtros (GatewayFilter Factories) e diversos filtros globais que cobrem desde a manipulação de headers e reescrita de caminhos até mecanismos complexos de rate limiting, circuit breaker e retentativas. Ao entender e utilizar esses recursos, é possível reduzir em até 80% o código boilerplate do gateway.
Arquitetura e Ciclo de Vida da Requisição
Para utilizar o Spring Cloud Gateway de forma eficiente, é fundamental compreender como ele processa cada requisição. O fluxo segue uma sequência lógica baseada em Programação Reativa (Project Reactor):
- RoutePredicateHandlerMapping: Identifica se a requisição atual corresponde a alguma rota configurada com base em predicados (Path, Host, Method, etc.).
- FilteringWebHandler: Combina os filtros específicos da rota com os filtros globais e os ordena.
- Cadeia de Filtros (Pre-phase): Executa a lógica antes de encaminhar a requisição ao serviço de destino.
- NettyRoutingFilter: Realiza o encaminhamento da requisição para o microserviço downstream usando o cliente HTTP do Netty.
- Cadeia de Filtros (Post-phase): Executa a lógica de retorno, processando a resposta antes de enviá-la ao cliente final.
Modelo Não-Bloqueante
Diferente do antigo Zuul 1.x, o Spring Cloud Gateway roda sobre o Spring WebFlux e o Reactor Netty. Isso significa que ele utiliza um modelo de Event Loop, onde poucas threads processam milhares de conexões simultâneas. Regra de ouro: Nunca execute operações bloqueantes (como JDBC ou Thread.sleep()) dentro de um filtro, sob risco de travar todo o gateway.
Categorias de Filtros Nativos Essenciais
Em vez de decorar todos os filtros, é mais produtivo classificá-los por sua responsabilidade funcional:
1. Manipulação de Protocolo e Cabeçalhos
Utilizados para higienizar requisições externas e propagar contextos de identidade ou rastreamento para os serviços internos.
AddRequestHeader/SetRequestHeader: Adiciona ou sobrescreve informações no cabeçalho.RemoveResponseHeader: Remove cabeçalhos sensíveis do serviço downstream antes de responder ao cliente.DedupeResponseHeader: Remove cabeçalhos duplicados (comum em problemas de CORS).
2. Reescrita de Caminhos (Path Transformation)
Permite desacoplar a URL pública da estrutura interna de APIs dos microserviços.
spring:
cloud:
gateway:
routes:
- id: servico_estoque
uri: lb://inventory-service
predicates:
- Path=/v1/estoque/**
filters:
- StripPrefix=1
- AddRequestHeader=X-Origin-Gateway, SCG-Production
No exemplo acima, uma requisição para /v1/estoque/produtos é encaminhada para o serviço de inventário como /produtos, removendo o prefixo inicial.
3. Resiliência e Estabilidade
Filtros que protegem o ecossistema de falhas em cascata e picos de tráfego.
RequestRateLimiter: Implementa controle de vazão (geralmente via Redis com algoritmo Token Bucket).Retry: Configura tentativas automáticas em caso de falhas específicas (ex: 503 Service Unavailable).CircuitBreaker: Integração com Resilience4j para interromper chamadas a serviços instáveis.
Implementando Lógica Customizada (Apenas quando necessário)
Quando a lógica de negócio exige uma validação que não pode ser resolvida via configuração, devemos implementar um GatewayFilterFactory ou um GlobalFilter. Abaixo, um exemplo de um filtro que monitora o tempo de processamento sem bloquear a thread:
@Component
public class MonitoramentoTempoFilter implements GlobalFilter, Ordered {
private static final String START_TIME_ATTR = "startTimeMillis";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// Fase PRE: Armazena o timestamp inicial
exchange.getAttributes().put(START_TIME_ATTR, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// Fase POST: Calcula a diferença e adiciona no log ou header
Long startTime = exchange.getAttribute(START_TIME_ATTR);
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
exchange.getResponse().getHeaders().add("X-Processing-Duration-MS", String.valueOf(duration));
}
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
Diferenças entre GatewayFilter e GlobalFilter
A escolha entre o tipo de filtro impacta diretamente no escopo de atuação:
| Tipo | Escopo | Uso Recomendado |
|---|---|---|
| GatewayFilter | Rota Específica | Transformações de payload ou headers exclusivas de um microserviço. |
| default-filters | Todas as Rotas via YAML | Políticas globais de segurança (ex: SecureHeaders) aplicadas via config. |
| GlobalFilter | Todas as Rotas via Código | Autenticação cantralizada, logging de auditoria e tracing. |
Melhores Práticas para o Ambiente Produtivo
Para manter um gateway saudável e de alta performance, siga estas diretrizes:
- Priorize Configuração: Se existe um filtro nativo (como
RewritePath), utilize-o em vez de criar um filtro Java. - Evite Log de Body: Ler o corpo da requisição/resposta (Request/Response Body) exige carregar os dados em memória, o que pode causar overhead e problemas de GC em alta carga.
- Gestão de Erros: Utilize o filtro
FallbackHeadersem conjunto comCircuitBreakerpara fornecer respostas amigáveis ao cliente quando o back end falhar. - Observabilidade: Aproveite a integração nativa com Micrometer para expor métricas de latência e taxa de erro por rota.