DevGuideTK
← Trilha Backend

Limite de requisições

Algoritmos para limitar o uso de uma API e proteger o serviço contra abuso e sobrecarga.

Para que limitar

Uma API tem capacidade finita: CPU, conexões com o banco, chamadas a serviços externos. Sem nenhum limite, essa capacidade é dividida por ordem de chegada, e quem chega com mais pedidos leva tudo.

AmeaçaExemplo
Cliente com defeitoUma integração presa num laço chamando a mesma rota mil vezes por segundo
Robôs e raspagemAlguém copiando o catálogo inteiro, página por página
Força brutaMilhares de tentativas de senha numa rota de login
Abuso de custoChamadas repetidas a uma rota que usa um serviço pago, como envio de SMS ou um modelo de IA
Picos legítimosUm parceiro que dispara uma carga enorme de uma vez

O limite de requisições (rate limiting) define quanto cada cliente pode usar num intervalo e recusa o excesso. A resposta padrão é o código 429 Too Many Requests, com a indicação de quando tentar de novo.

Quem é "o cliente"

Antes do algoritmo, a decisão é o que identifica cada cliente:

IdentificadorUsoCuidado
Chave de API ou usuário autenticadoO mais justo e precisoSó existe depois da autenticação
Endereço IPRotas públicas, como login e cadastroMuitos usuários atrás do mesmo IP (empresas, operadoras móveis); o IP real só vem confiável se o proxy for seu
CombinaçãoLogin: por conta e por IPMais chaves para contar

Os algoritmos

Janela fixa

O mais simples: um contador por cliente e por intervalo, como "pedidos do cliente 42 no minuto 14:05". Quando o minuto vira, o contador recomeça.

O problema está na virada. Um cliente pode usar o limite inteiro no fim de uma janela e o limite inteiro no começo da seguinte. Testamos com um limite de 10 pedidos por segundo, guardado no Redis: 10 pedidos aos 0,9 segundo e 10 aos 0,05 do segundo seguinte. Os 20 foram aceitos, num intervalo de 0,11 segundo, o dobro do limite.

Janela deslizante

Conta os pedidos dos últimos N segundos a partir de agora, e não do início do minuto. Elimina o problema da virada. A versão exata guarda o horário de cada pedido; a versão aproximada combina o contador da janela atual com uma fração da anterior, com memória fixa.

Balde de fichas

Cada cliente tem um balde com uma capacidade de fichas. Cada pedido gasta uma ficha. O balde é reabastecido num ritmo constante, até encher. Sem fichas, o pedido é recusado.

capacidade 10, reposição de 5 fichas por segundo

t = 0,0 s   balde cheio (10)  →  rajada de 20 pedidos: 10 aceitos, 10 recusados
t = 1,0 s   balde com 5       →  10 pedidos: 5 aceitos, 5 recusados

O balde permite rajadas curtas, até a capacidade, mas limita o ritmo médio. É o comportamento que a maioria das APIs quer: um cliente que carrega uma tela com 8 chamadas de uma vez não deve ser punido, mas não pode manter esse ritmo para sempre. Foi esse o algoritmo implementado e testado nas cinco linguagens, com o resultado exato do diagrama acima.

AlgoritmoRajadasMemória por clientePrecisão
Janela fixaAté o dobro do limite, na viradaUm contadorBaixa
Janela deslizante exataNãoUm registro por pedidoAlta
Janela deslizante aproximadaNãoDois contadoresBoa
Balde de fichasAté a capacidade, por desenhoDois valoresAlta

O problema das várias instâncias

Com a aplicação em várias cópias, como no tópico de escalabilidade, o contador precisa ser compartilhado. Um limite guardado na memória de cada instância é um limite por instância.

Testamos com duas cópias de um servidor atrás de um Nginx, cada uma com um limite de 10 pedidos por minuto na própria memória. De 30 pedidos do mesmo cliente, 20 foram aceitos: 10 por instância. Com dez instâncias, o limite efetivo seria dez vezes o configurado, e mudaria a cada escala automática.

A solução é guardar a contagem num lugar compartilhado, quase sempre o Redis.

Conferir e depois somar não funciona

A implementação mais intuitiva no Redis lê o contador, confere se está abaixo do limite e grava o novo valor. Com pedidos simultâneos, todos leem o mesmo valor antes de qualquer um gravar.

Testamos com 20 pedidos simultâneos e um limite de 10:

ImplementaçãoAceitos
GET, conferir, SET20
INCR atômico, conferindo o valor devolvido10

É a mesma lição do tópico de transações e do de idempotência: conferir e agir precisam ser uma única operação atômica. No Redis, isso significa um comando atômico como INCR ou um script Lua, que o Redis executa sem intercalar com outros comandos.

Por que isso importa quando a IA escreve o código

Um assistente que acrescenta limite de requisições costuma escolher o caminho mais curto:

  • Um contador num mapa em memória, que vira um limite por instância.
  • Janela fixa, com o dobro do limite na virada.
  • GET e SET separados no Redis, que deixam passar rajadas simultâneas.
  • Identificação pelo IP lido de X-Forwarded-For sem conferir se veio do proxy, permitindo que qualquer um troque de "identidade" a cada pedido.
  • Resposta 429 sem dizer quando tentar de novo, e clientes que tentam de novo na hora.

O próximo nível compara onde aplicar o limite, quais valores escolher e como comunicar a recusa.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Limite de requisições em cinco minutos

    Explique em no máximo cinco minutos de leitura o que é limite de requisições numa API, para alguém que nunca precisou proteger um serviço contra excesso de uso.
    
    Cubra só o essencial: por que limitar, como identificar cada cliente, a diferença entre janela fixa e balde de fichas, e por que a contagem precisa ser compartilhada entre as instâncias.
    
    No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
  • Os algoritmos de limite

    Quero entender os algoritmos de limite de requisições.
    
    Aja como um mentor. Explique, um de cada vez, com uma linha do tempo de pedidos:
    1. Janela fixa, e o problema da virada.
    2. Janela deslizante exata.
    3. Janela deslizante aproximada.
    4. Balde de fichas.
    5. Balde furado (leaky bucket).
    
    Para cada um, me dê uma sequência de pedidos e peça que eu diga quais são aceitos, antes de mostrar a resposta. No final, monte comigo uma tabela comparando rajadas, memória e precisão.
  • Limites com várias instâncias

    Quero entender por que um limite de requisições guardado em memória falha quando a aplicação tem várias instâncias.
    
    Explique:
    1. O que acontece com um limite em memória atrás de um balanceador com duas, dez e um número variável de instâncias.
    2. Por que guardar a contagem no Redis resolve, e por que ler e depois gravar no Redis não resolve.
    3. Como um comando atômico ou um script Lua garantem a contagem correta.
    4. O problema dos relógios diferentes entre as máquinas, e como o relógio do Redis resolve.
    
    Depois me faça três perguntas de cenário e corrija as minhas respostas.
  • Quem é o cliente

    Quero entender como identificar o cliente para aplicar limites de requisições.
    
    Explique as opções: chave de API, usuário autenticado, endereço IP e combinações.
    
    Para o IP, explique em detalhe:
    1. Por que muitos usuários podem compartilhar o mesmo IP.
    2. Como o IP real chega atrás de um proxy, e por que o cabeçalho X-Forwarded-For pode ser falsificado.
    3. Como configurar a aplicação para confiar nele só quando vier do proxy.
    
    Depois me apresente cinco rotas (login, cadastro, busca pública, API de parceiros, envio de SMS) e pergunte como eu identificaria o cliente em cada uma. Corrija as minhas respostas.