- A Uber construiu um Global Rate Limiter (GRL) em um ambiente com milhares de microsserviços que processam centenas de milhões de RPCs por segundo, estabelecendo uma estrutura unificada de proteção contra sobrecarga
- O GRL usa uma arquitetura de loop de feedback em 3 camadas composta por cliente, agregador e controlador, permitindo decidir localmente sobre as requisições e ainda assim coordenar globalmente em poucos segundos
- A empresa migrou do modelo inicial de token bucket para um modelo de descarte probabilístico, resolvendo problemas de justiça e escalabilidade e minimizando a latência no hot path
- Em comparação com limitadores baseados em Redis, a latência P99.5 melhorou em até 90%, e o sistema absorveu picos de tráfego de 15x e ataques DDoS sem degradação do serviço
- O Rate Limit Configurator (RLC) analisa padrões históricos de tráfego e atualiza automaticamente os limites, evoluindo de uma configuração estática para um sistema autoajustável
Problemas do rate limiting tradicional
- No ambiente inicial de microsserviços da Uber, cada equipe implementava throttling por conta própria usando lógica de negócio, middleware customizado e contadores baseados em Redis
- Isso gerou problemas como configurações inconsistentes, latência extra causada pelo Redis, necessidade de redeploy dos servidores ao alterar limites e dificuldade de responder a incidentes por causa de limitadores não documentados
- Muitos serviços menores operavam sem qualquer rate limiting, e cada limitador reportava métricas e erros de forma diferente, inviabilizando uma observabilidade unificada
- Usar o Redis como contador centralizado causava latência inaceitável e problemas de consistência entre regiões em um ambiente com centenas de milhares de hosts, centenas de milhões de requisições por segundo e múltiplas regiões
- Mesmo com sharding e replicação, seriam necessários centenas de clusters Redis, além de introduzir novos modos de falha
- A abordagem de sincronização periódica dos contadores também foi descartada: reduzia o overhead de rede, mas sofria com dados desatualizados e resposta lenta a picos bruscos de tráfego
- A conclusão foi que apenas uma arquitetura totalmente distribuída, em que proxies locais decidem com base na carga agregada, poderia atingir ao mesmo tempo baixa latência e escalabilidade global
Visão de um limitador unificado no nível da infraestrutura
- A solução foi embutir o rate limiting na service mesh da Uber, a camada de infraestrutura responsável pelo tráfego RPC entre serviços
- Ao incorporar o limitador nessa camada, tornou-se possível inspecionar e avaliar todas as requisições independentemente da linguagem ou framework do chamador, antes de chegarem ao destino
- Objetivo: oferecer um serviço unificado de rate limiting no qual as equipes possam definir cotas por chamador e por procedimento sem alterar código
- Era necessário escalar para centenas de milhões de requisições por segundo, dezenas de milhares de pares de serviço e frotas de hosts em múltiplas regiões geográficas com mínimo impacto de latência
Arquitetura do GRL: loop de feedback em 3 camadas
- O núcleo do GRL é uma estrutura de loop de feedback em 3 camadas
- Cliente de rate limit (data plane da service mesh): toma decisões localmente para cada requisição com base nas instruções recebidas do agregador e reporta o número de requisições por segundo por host ao agregador no nível da zona
- Agregador (por zona): coleta métricas de todos os clientes da mesma zona, calcula o uso no nível da zona e envia os dados ao controlador
- Controlador (por região, global): agrega os dados das zonas, determina a utilização global e propaga de volta para agregadores e clientes as instruções atualizadas de taxa de descarte
- Essa agregação hierárquica permite manter baixa latência no hot path (já que a decisão é local) e, ao mesmo tempo, obter coordenação global em poucos segundos
- Em caso de falha no control plane, os clientes operam em modo fail-open, deixando o tráfego passar para evitar falhas autoinfligidas
Evolução da lógica de rate limiting
-
Modelo inicial com token bucket
- No início, cada proxy do data plane de rede aplicava o algoritmo de token bucket
- Cada proxy rastreava o volume local de requisições, repunha tokens ao longo do tempo e permitia ou negava requisições conforme a quantidade disponível
- A taxa de reposição de tokens era calculada a partir da razão entre a carga local do proxy e o limite global:
ratio × limitRPS
- Para lidar com tráfego em burst, tokens não utilizados eram armazenados em um buffer circular, mantidos por padrão por 10 segundos (configurável até 20 segundos)
- Em produção, surgiram problemas de justiça e escalabilidade: quando o número de chamadores ultrapassava o limite, não era possível distribuir a capacidade de forma justa; além disso, bursts em hosts individuais causavam descartes precoces mesmo abaixo do limite global
-
Introdução do Drop-by-Ratio
- Quando a carga global agregada ultrapassa o limite configurado, os clientes descartam probabilisticamente uma fração das requisições
- Exemplo: se o RPS agregado de um chamador é 1,5x o limite, todas as instâncias descartam cerca de 33%; fórmula:
drop_ratio = (actual_rps - limit_rps) / actual_rps
- Com esse sinal global de descarte atualizado pelo control plane a cada poucos segundos, o tráfego excedente é limitado de forma uniforme em todas as instâncias do chamador
- O modelo foi especialmente eficaz em serviços grandes no estilo gateway, com centenas ou milhares de instâncias chamadoras
-
Transição para um modelo probabilístico unificado
- À medida que o GRL amadureceu, a Uber abandonou totalmente o token bucket e unificou tudo em um modelo de descarte probabilístico baseado no control plane
- Operar os dois algoritmos ao mesmo tempo aumentava a complexidade da configuração e o overhead de rede
- Ao consolidar em um único modelo, a empresa simplificou a configuração, reduziu o consumo de banda do control plane e unificou todas as decisões de rate limiting sob um mecanismo globalmente consistente
- Trade-off: por depender de dados globais agregados atualizados a cada segundo, há um atraso de aplicação de 2 a 3 segundos
- Na prática, isso é irrelevante para a maioria das cargas de trabalho, afetando apenas bursts extremamente curtos e intensos
-
Design final: descarte probabilístico orientado pelo control plane
- No GRL atual, a aplicação acontece inteiramente na camada cliente do data plane de rede
- Fluxo de processamento quando uma requisição chega:
- A requisição é associada ao bucket configurado (definido por chamador, procedimento ou ambos)
- Se houver uma instrução ativa de taxa de descarte para esse bucket, ocorre descarte probabilístico conforme a taxa definida
- Se não houver instrução de descarte, a requisição é encaminhada normalmente
- O hot path é extremamente leve: não há cálculo local de tokens nem comunicação com o control plane por requisição; a decisão in-process usa apenas amostragem probabilística simples
- Agregadores e controladores executam fora do plano de entrega os cálculos complexos (agregação de contagem de requisições, comparação com limiares e cálculo de novas taxas de descarte) a cada segundo
- Esse design permite escalar para centenas de milhões de requisições por segundo mantendo precisão de aplicação global em poucos segundos
Configuração de limites
- Os donos de serviço definem buckets de rate limiting em arquivos de configuração
- Escopo: global, por região, por zona
- Regras de correspondência: nome do chamador, procedimento ou ambos
- Comportamento: deny (aplica) ou allow (modo shadow para testes)
- A aplicação é transparente no serviço de destino sem necessidade de alterar código
Resultados operacionais
-
Redução de latência e eliminação de overhead
- Antes do GRL, muitos serviços usavam limitadores baseados em Redis que exigiam uma ida e volta de rede por requisição
- Com a avaliação local no data plane da service mesh, esse hop adicional foi eliminado, reduzindo significativamente a latência
- A latência P50 caiu cerca de 1 ms, a P90 caiu dezenas de ms e a P99.5 passou de centenas de ms para dezenas de ms, com melhora de até 90%
-
Simplificação operacional e eficiência de recursos
- Centralizar o rate limiting no data plane da service mesh simplificou a infraestrutura
- Não é necessário um datastore ou camada de cache dedicada para aplicar cotas
- A desativação de um grande número de instâncias Redis antes dedicadas ao rate limiting trouxe ganhos relevantes de eficiência computacional
-
Maior estabilidade e resposta a incidentes
- Desde a implantação, o GRL tem evitado repetidamente sobrecargas durante picos, failovers e tempestades de retry
- Ao fazer shedding probabilístico do tráfego excedente na service mesh, o sistema mantém tempos de resposta consistentes mesmo com aumentos bruscos de carga
- Um serviço crítico suportou sem degradação um salto de tráfego de 22 mil para 367 mil RPS, ou seja, 15x
- O sistema absorveu ataques DDoS antes que chegassem aos sistemas internos
- Durante incidentes, a equipe de engenharia de produção aplica com o GRL rate limits direcionados a chamadores ou procedimentos específicos de alto tráfego, e como as atualizações do control plane se propagam a cada segundo, a sobrecarga pode ser contida em poucos segundos
- Isso permite throttling rápido e seguro de padrões específicos de tráfego sem redeploy dos serviços
- Em escala total, o sistema aplica cotas dinâmicas a cerca de 80 milhões de requisições por segundo em mais de 1.100 serviços
Automação do rate limiting: Rate Limit Configurator (RLC)
-
Limitações da configuração manual
- Embora o GRL tenha unificado a aplicação, a definição de limites ainda exigia trabalho manual
- Os donos de serviço definiam cotas por chamador e procedimento em arquivos YAML e ajustavam esses valores sempre que os padrões de tráfego mudavam
- Em um ambiente com centenas de microsserviços evoluindo continuamente, configurações estáticas rapidamente ficavam desatualizadas
- Se forem rígidas demais, causam throttling até em picos normais de tráfego
- Se forem flexíveis demais, oferecem pouca proteção por ficarem muito acima da capacidade real
- Mudanças dependiam de análise em dashboards e ajuste manual
-
Como o RLC funciona
- O RLC (Rate Limit Configurator) mantém automaticamente as configurações do GRL atualizadas
- Em uma agenda fixa ou imediatamente após mudanças de configuração, ele executa o próximo ciclo:
- Coleta métricas das últimas semanas a partir da plataforma de observabilidade da Uber
- Calcula limites seguros por chamador e procedimento com base em picos históricos e margem de buffer
- Grava a configuração atualizada em um repositório compartilhado de configuração
- Envia os novos limites ao GRL por meio do control plane existente
- Com esse processo em loop fechado, os limites evoluem junto com o tráfego real, minimizando a intervenção manual
-
Design escalável
- O RLC foi projetado desde o início para suportar múltiplas estratégias de cálculo de rate limit
- A política padrão se baseia em dados históricos de RPS, mas novos tipos de política podem ser adicionados de forma modular
- Serviços como os de dados de mapas e localização usam modelos preditivos que incorporam previsão de tráfego e capacidade planejada, estimando carga futura em vez de apenas seguir tendências passadas
- O sistema também suporta atribuição de cotas fixas definidas por acordos contratuais ou operacionais previamente estabelecidos
- Por meio de interfaces modulares, cada domínio de serviço pode escolher a lógica de cálculo mais adequada entre padrões de tráfego quase em tempo real, previsão ou cotas estáticas
-
Modo shadow e validação
- Por segurança, é possível gerar e monitorar limites em modo shadow, sem aplicá-los
- Os donos de serviço observam o comportamento do rate limiting em produção antes de ativá-lo
- Dashboards e alertas dedicados visualizam o tráfego observado e os descartes virtuais, aumentando a confiança antes do rollout
-
Efeitos da automação
- Milhares de regras de rate limiting são atualizadas automaticamente sem edição manual
- As políticas passam a ser geradas com consistência em todos os serviços, usando a mesma fórmula e a mesma fonte de dados
- Com diferentes tipos de política, as equipes podem escolher e expandir a lógica de cálculo mais adequada para cada workload
- O modo shadow ajuda a garantir a precisão antes da aplicação
Próximos passos
- Mesmo após a introdução do RLC, a Uber continua ampliando o ajuste de buffer, introduzindo rate limiting por região para reduzir o impacto de mudanças de configuração e aumentando a frequência de atualização para melhorar a resposta ao tráfego ao vivo
- A camada de throttler da Uber oferece proteção adicional contra sobrecarga mais próxima da aplicação
- Hoje, o GRL é um componente central da pilha multicamadas de estabilidade da Uber, mantendo a estabilidade e a justiça da plataforma mesmo sob carga extrema
Ainda não há comentários.