Skip to content
This page has been auto-translated and may contain errors.View in English

Contadores de janela fixa

Um estacionamento com cinco vagas e uma cancela. A cada trinta segundos a cancela reabre e as vagas ficam livres de novo.

Isso é um contador de janela fixa, o algoritmo de rate limiting mais simples que existe. Conte requisições dentro de uma janela de duração fixa, recuse tudo que passar do limite, e reinicie a contagem quando a janela terminar.

Observando uma janela se encher

Um limite de cinco requisições por janela de trinta segundos:

TempoO que chegaContagemResultado
0s1 requisição1Aceita
5s2 requisições3Aceita
15s2 requisições5Aceita, e a cancela desce
25s1 requisição5Rejeitada, a janela está cheia
30sJanela 2 abre0A contagem reinicia

O estado envolvido são dois valores: em que ponto da janela você está e a contagem até agora. Nada é carregado da janela anterior.

Essa simplicidade é o atrativo inteiro. Barato para armazenar, barato para verificar, e difícil de gerar confusão.

JunoObservando uma janela se encher Repare na requisição dos 25 segundos. Ela é recusada faltando cinco segundos para acabar, e se tivesse esperado mais seis, teria passado tranquilamente.

Isso é o algoritmo sendo direto, não injusto. Ele não tem noção de "quase", só de "dentro desta janela" ou não.

JunoObservando uma janela se encher "Nada é lembrado da janela anterior" é o que torna isso barato de rodar. Um contador e um timestamp por cliente, e um reset que descarta o valor antigo.

Compare isso com manter um timestamp para cada requisição, que é o que o próximo algoritmo exige. Essa diferença de armazenamento é o que sustenta toda a comparação.

JunoObservando uma janela se encher Há um segundo problema junto do estouro, e ele é a imagem espelhada: a seca. Cinco requisições chegando no primeiro segundo de uma janela deixam vinte e nove segundos em que um cliente legítimo é recusado mesmo com o serviço ocioso.

Janelas fixas são, portanto, duras com tráfego em rajadas mas razoável, que é a maior parte do tráfego real. Uma página carregando seis recursos de uma vez gasta o orçamento de uma janela inteira instantaneamente.

Também vale decidir se as janelas são alinhadas ao relógio ou à primeira requisição de cada cliente. Janelas alinhadas ao relógio fazem a janela de todo cliente reiniciar no mesmo instante, o que sincroniza as tentativas de todo mundo em um único pico.

O estouro na fronteira

Agora observe duas janelas consecutivas, com o mesmo limite de cinco a cada trinta segundos:

TempoJanelaRequisiçõesContagem acumulada
50s224
55s226, então 5 aceitas
60s3 abre22

Olhe para o relógio, não para as janelas. Entre 50 e 60 segundos, sete requisições foram aceitas.

O limite era cinco a cada trinta segundos, e sete chegaram em dez.

Nada funcionou mal. Requisições no fim da janela dois e no início da janela três estão em janelas diferentes, então nenhuma contagem passou de cinco. O algoritmo fez exatamente o que foi programado para fazer.

Esse é o problema da fronteira da janela, e é a fraqueza definidora dessa abordagem. No pior caso, um cliente envia o limite total imediatamente antes de um reset e o limite total imediatamente depois, passando o dobro do limite em um instante.

JunoO estouro na fronteira O truque está em onde você está olhando. Dentro de cada janela, tudo está correto e dentro do limite.

Dê um passo atrás e olhe para um trecho de tempo real que cruza a fronteira, e o limite nunca foi realmente aplicado ali.

JunoO estouro na fronteira Vale colocar um número nisso: o pior caso é o dobro do seu limite em um instante, então dimensione o limite pensando nisso. Se o seu serviço aguenta 200 requisições em rajada, um limite de janela fixa de 100 é defensável.

Um atacante que conhece a duração da sua janela pode acertar essa fronteira de propósito, e a duração da janela é simples de inferir observando quando o reset acontece.

JunoO estouro na fronteira Isso é exatamente reproduzível, não apenas teórico. Medindo contra um limite de janela fixa de 100, enviar através de uma fronteira permite passar 200 requisições.

O sliding window log permite 100 e o sliding window counter permite 101, e é essa a comparação sobre a qual os próximos capítulos se apoiam.

O motivo para continuar usando janelas fixas mesmo assim é o custo: um inteiro e um timestamp por cliente, incrementados atomicamente. Na escala em que rate limiting importa mais, essa diferença em armazenamento e coordenação não é pequena.

A resposta usual em produção é uma janela fixa dimensionada de forma que o dobro do limite seja sustentável, com algo mais preciso nos poucos endpoints onde uma rajada causa dano real.

Coloque em prática

Uma API permite 10 requisições por janela de 60 segundos, alinhada ao relógio, de forma que as janelas começam no início de cada minuto.

  1. Um cliente envia 10 requisições às 12:00:59 e mais 10 às 12:01:01. Quantas são aceitas, e o limite foi quebrado?
  2. Um cliente envia todas as 10 às 12:00:00. O que acontece com a requisição dele às 12:00:30?
  3. Você precisa garantir que nenhum cliente receba mais de 10 requisições em qualquer intervalo de 60 segundos. Uma janela fixa consegue fazer isso?
Compare suas respostas

1. Todas as 20 são aceitas, e nenhuma regra foi quebrada. As primeiras dez caem na janela das 12:00 e as segundas dez na das 12:01, então nenhuma contagem passou de 10.

Vinte requisições chegaram em dois segundos. Isso é o estouro na fronteira, e não exigiu nada mais sofisticado do que perceber quando o minuto vira.

2. Rejeitada. A janela está cheia e ainda faltam trinta segundos para ela terminar. O serviço está completamente ocioso e o cliente mesmo assim espera, que é a seca que vem junto com a rajada.

3. Não. Não com uma janela fixa, em nenhum limite. Qualquer intervalo de tempo real que cruze uma fronteira pode carregar até o dobro do limite, e isso é uma propriedade de dividir o tempo em blocos fixos, não um problema de ajuste fino.

Conseguir essa garantia significa medir contra uma janela que se move junto com a requisição, em vez de um bloco preso a um calendário, que é o assunto do próximo capítulo.

Para onde isso vai a seguir

Janelas fixas são baratas, simples, e erradas por até um fator de dois na fronteira. Se isso importa depende do que o endpoint faz.

Antes de consertar isso, vale a pena construir uma. Construindo um rate limiter transforma esse algoritmo em um middleware Express funcional, com os headers que um cliente precisa para se comportar bem.