Rilasciato · in miglioramento
Algorithm
Il rate limiting limita le richieste di un utente o di un IP per finestra di tempo: token bucket, finestra scorrevole, leaky bucket e Redis.
Un rate limiter controlla quante richieste vengono accettate in un certo intervallo di tempo. Si definiscono una chiave, un limite e una finestra, per esempio "100 richieste al minuto per utente", e le richieste oltre il limite vengono rifiutate o ritardate. Gli algoritmi classici sono finestra fissa, log a finestra scorrevole, contatore a finestra scorrevole, token bucket e leaky bucket, e si distinguono per precisione, uso di memoria e gestione dei burst.
Quasi ogni funzione esposta all'esterno, dalle API pubbliche al login, dai pagamenti all'invio di messaggi, è protetta da un rate limiter. Difende i server da picchi di traffico e da cicli di retry fuori controllo, impedisce a un singolo utente di monopolizzare la capacità condivisa e rallenta gli attacchi a forza bruta. È anche un tema ricorrente nei colloqui di system design.
Inizia scrivendo un contatore a finestra fissa e osserva come lascia passare il doppio del limite al confine tra due finestre, poi correggilo con una finestra scorrevole e un token bucket. Prosegui imparando a testare con un orologio iniettato, a rispondere HTTP 429 con Retry-After e a condividere un limite tra più server con INCR, EXPIRE e gli script Lua di Redis.
I token si ricaricano a velocità costante e ogni richiesta ne consuma uno: si rispetta una velocità media e si ammettono burst fino alla capacità del secchio.
Un log di timestamp, o una media pesata di due contatori, conta le richieste recenti senza il picco al confine della finestra fissa.
Limiti separati per ID utente, chiave API, indirizzo IP o endpoint impediscono a un singolo client di monopolizzare le risorse.
L'INCR atomico di Redis e gli script Lua permettono a molti server di condividere un limite senza race condition.
TokenBucket ricarica rate token al secondo e ne contiene al massimo capacity. allow() accredita in un colpo solo tutti i token maturati dall'ultima chiamata (ricarica pigra) e ne consuma uno se ce ne sono abbastanza. Eseguendo lo script, le prime 5 chiamate passano come burst e le 3 successive vengono rifiutate; dopo una pausa di 1,1 secondi ne passano altre due. Il tempo è misurato con time.monotonic(), che non torna mai indietro.
token_bucket.py
import time
class TokenBucket:
"""Allow bursts of up to `capacity` requests, refilled at `rate` tokens per second."""
def __init__(self, rate: float, capacity: float) -> None:
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.updated = time.monotonic()
def allow(self, cost: float = 1.0) -> bool:
now = time.monotonic()
self.tokens = min(self.capacity, self.tokens + (now - self.updated) * self.rate)
self.updated = now
if self.tokens >= cost:
self.tokens -= cost
return True
return False
if __name__ == "__main__":
bucket = TokenBucket(rate=2, capacity=5) # 2 requests per second, bursts of 5
print([bucket.allow() for _ in range(8)]) # 5 x True, then 3 x False
time.sleep(1.1) # about 2.2 tokens come back
print(bucket.allow(), bucket.allow(), bucket.allow()) # True True False
python token_bucket.pySei capitoli che ti accompagnano dall'installazione ai concetti chiave di Rate limiting.
Fai domande, condividi la tua esperienza e scambia opinioni su Rate limiting.
Ancora nessuna discussione. Avvia la prima.
0 commenti
Accedi · Accedi per lasciare un commento.
Scrivi tu il primo commento.