API travando sob pico? Como implementar rate limiting com Redis
Descubra como proteger sua API contra picos de tráfego e ataques de negação de serviço utilizando Redis e o algoritmo de janela deslizante em microsserviços.
Resumo
- O controle de tráfego evita que picos inesperados derrubem servidores de backend e protegem contra ataques de negação de serviço.
- O Redis oferece alta velocidade em memória para armazenar contadores de requisições de forma distribuída.
- O algoritmo de janela deslizante garante precisão matemática superior ao modelo tradicional de contagem fixa.
- Blocos de código em Lua executam operações atômicas no Redis, eliminando condições de corrida entre servidores.
- Ajustar limites por usuário ou IP preserva a experiência de clientes legítimos mesmo durante sobrecargas.
Por que APIs travam e como o controle de tráfego salva seu backend
Imagine que sua aplicação receba repentinamente dez vezes mais acessos do que o normal, seja por uma campanha de marketing bem-sucedida ou por um ataque malicioso automatizado. Sem uma barreira de proteção, o servidor de backend esgota suas conexões com o banco de dados e simplesmente para de responder. Na prática, isso significa que clientes legítimos ficam sem serviço e sua infraestrutura sofre instabilidade severa. Para evitar esse colapso, utilizamos o conceito de rate limiting, que consiste em limitar o número máximo de requisições que um cliente pode fazer em um determinado intervalo de tempo.
Quando construímos sistemas distribuídos modernos baseados em microsserviços, contar requisições na memória de uma única máquina deixa de funcionar, pois as solicitações chegam a diferentes nós. É exatamente aqui que entra a necessidade de uma solução centralizada e veloz. Como vimos em nossa discussão anterior sobre como usar filas assíncronas, desacoplar o fluxo de dados ajuda a absorver volume, mas o controle de entrada precisa acontecer na borda antes que o processamento pesado comece. Proteger a aplicação exige uma estratégia robusta baseada em cache distribuído e contadores atômicos.
O papel do Redis e o desafio do armazenamento distribuído
O Redis é um banco de dados em memória extremamente rápido, frequentemente utilizado como cache ou barramento de mensagens. Na arquitetura de controle de tráfego, ele atua como a fonte única da verdade para registrar quantas vezes um determinado usuário ou endereço IP realizou requisições recentemente. Em vez de consultar discos rígidos lentos, a aplicação valida a permissão em frações de milissegundo. Essa velocidade é indispensável, pois o mecanismo de proteção não pode adicionar lentidão perceptível ao ciclo de vida da API.
No entanto, gerenciar contadores concorrentes em um ambiente distribuído apresenta armadilhas sutis. Se dois servidores tentarem ler e atualizar o mesmo contador exatamente ao mesmo tempo, pode ocorrer uma condição de corrida onde atualizações são perdidas. Para resolver esse problema com elegância, utilizamos scripts escritos em linguagem Lua que executam diretamente dentro do Redis de forma atômica, garantindo que nenhuma requisição escape da contagem correta. Esse cuidado técnico assegura que a política de restrição funcione com precisão cirúrgica sob qualquer nível de concorrência.
Como funciona o algoritmo de janela deslizante na prática
Existem várias abordagens para calcular limites de tráfego, sendo a janela fixa a mais simples, porém a mais problemática. Na janela fixa, se o limite é de cem requisições por minuto, o contador é zerado exatamente no início de cada minuto. Isso cria uma falha grave conhecida como efeito de borda: um usuário pode gastar todas as cem requisições nos últimos segundos de um minuto e mais cem requisições logo no primeiro segundo do minuto seguinte, acumulando duzentas chamadas em um intervalo curtíssimo. Para eliminar essa brecha, adotamos o algoritmo de janela deslizante.
A janela deslizante calcula o tráfego considerando uma proporção do minuto anterior somada ao minuto atual, suavizando o fluxo de requisições ao longo do tempo. Na prática, o algoritmo impede picos artificiais nas viradas de período, distribuindo o limite de forma contínua e justa. Essa precisão é comparável ao rigor técnico que aplicamos quando tratamos validações estritas em sistemas de inteligência artificial para barrar comportamentos inesperados. O ganho em estabilidade operacional compensa com folga a pequena complexidade adicional de implementação.
Implementando a janela deslizante com Redis e script Lua
Para colocar a teoria em código funcional, vamos utilizar uma estrutura que combina hashes e listas ordenadas no Redis, gerenciadas por um script Lua atômico. Esse script recebe o identificador do usuário, o limite máximo permitido e a janela de tempo em segundos, retornando se a requisição pode prosseguir ou se deve ser rejeitada com o código HTTP adequado.
local key = KEYS[1]local now = tonumber(ARGV[1])local window = tonumber(ARGV[2])local limit = tonumber(ARGV[3])local clear_before = now - windowredis.call('ZREMRANGEBYSCORE', key, 0, clear_before)local current_requests = redis.call('ZCARD', key)if current_requests < limit then redis.call('ZADD', key, now, now) redis.call('EXPIRE', key, window) return 1else return 0endNeste código Lua, removemos primeiro todos os registros de requisições mais antigos que a janela de tempo atual usando o comando de remoção por pontuação. Em seguida, contamos quantos registros restaram na lista ordenada. Se o número for menor que o limite estabelecido, adicionamos o registro atual com a marca temporal vigente, renovamos o tempo de expiração da chave para evitar consumo infinito de memória e permitimos a passagem. Caso contrário, recusamos a chamada imediatamente.
Boas práticas para mitigar falsos positivos e proteger clientes legítimos
Implementar rate limiting exige cuidado para não punir clientes legítimos que utilizam conexões corporativas compartilhadas atrás do mesmo endereço IP. Uma prática recomendada é combinar o endereço IP com tokens de autenticação ou identificadores de usuário logado na chave do Redis. Assim, se uma empresa inteira acessar sua API a partir do mesmo roteador, cada funcionário terá seu próprio limite isolado, evitando que o uso intenso de um colega derrube o acesso dos demais.
Outro ponto crítico é definir claramente qual resposta HTTP retornar quando o limite for atingido. O padrão da indústria é utilizar o código HTTP 429 Too Many Requests, acompanhado de cabeçalhos informativos como Retry-After, indicando exatamente em quantos segundos o cliente poderá tentar novamente. Essa transparência melhora a experiência de desenvolvedores que consomem sua API e evita reentradas agressivas automáticas que piorariam ainda mais a carga sobre o backend. Com monitoramento adequado e ajustes finos baseados no comportamento real do tráfego, sua infraestrutura se mantém resiliente sob qualquer circunstância.