Praktický návod

Jak implementovat rate limiting API

Omez rychlost před drahou prací a dej klientovi jasně vědět, kdy může pokračovat.

30 minut · API a Redis

Nejdřív stručně

Co rate limit chrání?

Rate limiting omezuje počet requestů za časové období. Chrání API před náhodnou smyčkou, příliš agresivním klientem i částí útoků, ale nenahrazuje autentizaci ani ochranu infrastruktury.

Throttling může požadavky zpomalovat nebo řadit do fronty; tvrdý limit je po vyčerpání odmítne. Sdílený Redis drží počitadlo konzistentní pro všechny běžící instance aplikace.

Připrav si

Co musíš zvolit před implementací

Číslo bez kontextu není politika. Urči, koho, co a v jakém okně omezuješ.

  • Identitu klienta: ideálně účet nebo ID API klíče. IP adresa je vhodná jen jako doplňkový limit pro anonymní provoz.
  • Rozsah limitu: celý účet, konkrétní endpoint nebo nákladná operace. Přihlášení potřebuje jinou politiku než čtení katalogu.
  • Kapacitu a časové okno, například 100 requestů za 60 sekund, odvozené z reálné kapacity a legitimního provozu.
  • Sdílený Redis dostupný všem instancím aplikace a rozhodnutí, co se stane při jeho výpadku.

Kroky 1 až 3

Vynucuj limit atomicky a předvídatelně

Začni pevným oknem. Je jednoduché na provoz i vysvětlení; plynulejší token bucket přidej až podle skutečné potřeby.

1. Definuj klíč a pravidla limitu

  1. Sestav klíč z verze politiky, stabilního ID klienta a názvu skupiny endpointů. Neukládej do klíče celý tajný token.
  2. Časové okno počítej na serveru. Hodiny klienta ani jeho vlastní hlavičky nesmí rozhodovat o limitu.
  3. Rate limiter spusť hned po bezpečném určení identity a před databázovým dotazem, externím API nebo jinou drahou prací.
  4. Zdokumentuj kapacitu i rozsah. Klient musí vědět, zda sdílí limit mezi všemi endpointy a zda se počítají neúspěšné requesty.
composer require predis/predis
Oficiální Redis dokumentace k PHP klientům

2. Zvyš počitadlo atomicky v Redisu

  1. Pro jednoduché pevné okno zvyš INCR a při prvním requestu nastav expiraci. Obě operace proveď jedním Lua skriptem, aby mezi nimi nezůstal klíč bez TTL.
  2. Skript vrátí aktuální počet a zbývající TTL. Request povol, pokud počet nepřekročil kapacitu.
  3. Redis klíč nech po konci okna automaticky zaniknout. Nepotřebuješ samostatný úklid počitadel.
  4. Pevné okno dovolí krátkou špičku na hranici dvou oken. Pokud je to problém, použij sliding window nebo token bucket.
local current = redis.call('INCR', KEYS[1])
if current == 1 then
    redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return {current, redis.call('TTL', KEYS[1])}
Oficiální Redis návod k rate limitingu

3. Vrať správnou HTTP odpověď

  1. Po překročení limitu vrať HTTP stav 429 Too Many Requests a krátké chybové tělo ve stejném formátu jako ostatní chyby API.
  2. Do hlavičky Retry-After dej počet sekund do konce okna. Klient tak nemusí hádat, kdy má request zopakovat.
  3. Pokud je podporuje tvůj ekosystém, můžeš přidat navrhované RateLimit a RateLimit-Policy hlavičky. Klient pak uvidí kapacitu, zbývající kvótu a čas do obnovení.
  4. Při výpadku Redisu se vědomě rozhodni mezi fail-open a fail-closed. Přihlášení nebo placená operace může potřebovat přísnější chování než veřejné čtení.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 17

{"error":"rate_limit_exceeded"}
Aktuální IETF návrh RateLimit hlaviček pro HTTP

Krok 4

Ověř limit i pod souběhem

Sekvenční test neodhalí závod mezi několika PHP procesy. Pošli requesty paralelně a sleduj společné počitadlo.

  1. Vyčerpej malý testovací limit

    V testovacím prostředí nastav limit na 5. Prvních pět requestů má projít a následující má vrátit 429 s kladným Retry-After.

    seq 1 8 | xargs -n1 -P8 -I{} curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/api/products
  2. Ověř izolaci klientů

    Vyčerpej limit jedním testovacím účtem a zavolej endpoint jiným. Druhý účet nemá sdílet jeho počitadlo, pokud to politika výslovně neříká.

  3. Počkej na obnovení okna

    Po uplynutí Retry-After musí další request projít. Zkontroluj také, že starý Redis klíč zmizel a neunikají klíče bez expirace.

    redis-cli TTL rate-limit:v1:test-user:catalog

Když to zlobí

Nejčastější chyby

Každá instance počítá requesty zvlášť

Nedrž produkční limit jen v paměti PHP procesu. Všechny instance musí používat stejný Redis a stejný formát klíče.

V Redisu zůstávají klíče bez expirace

INCR a první EXPIRE proveď atomickým Lua skriptem. Sleduj TTL a počet persistentních rate-limit klíčů.

Uživatel za firemní NAT zablokuje ostatní

Po přihlášení limituj podle stabilního ID účtu nebo API klíče. IP limit nech jako širší doplňkovou ochranu, ne jako jedinou identitu.

Klient po 429 posílá ještě více requestů

Retry musí respektovat Retry-After, mít horní mez pokusů a přidat náhodný jitter. Jinak všichni klienti zkusí pokračovat ve stejný okamžik.

Hotovo

API má sdílený a čitelný limit.

Rate limiting teď chrání drahou práci atomicky a klient dostává správnou HTTP odpověď. Limity dál upravuj podle metrik, ne podle odhadu.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.