Token Bucket Rate Limiter
L'algorithme du Rate Limiter qui autorise des bursts contrôlés sans renoncer à un débit soutenu maîtrisé : le détail qui transforme une politique en chiffre lisible.
Problème
Le Rate Limiter de base limite par fenêtre fixe (« 100 requêtes par minute »). Problème : en bordure de fenêtre, on peut tirer 100 requêtes à 12:00:59 et 100 à 12:01:00, soit 200 en une seconde — saturation. Autre problème : si on impose « jamais plus de 2 par seconde », un envoi de batch légitime de 50 INVOIC en une seconde, qui tient parfaitement en moyenne sur la journée, est rejeté. Il faut un algorithme qui distingue débit soutenu et burst, et qui ne crée pas d'effet de bord temporel.
Forces
- Les pics légitimes existent. Fin de mois, synchronisation matinale, batches partenaires.
- Le débit soutenu doit être borné. Sur 24h, on ne veut pas dépasser X millions de requêtes.
- Le calcul doit être simple. Le Rate Limiter tourne avant tout traitement, doit être O(1).
- Le SLA partenaire doit être calculable. « Tu peux burster à 500, débit moyen 100/s » est contractuel.
Solution
Implémenter un seau de jetons de capacité maximale N (taille du burst autorisé), rechargé à un taux R jetons/seconde (débit soutenu). Chaque requête
consomme 1 jeton (ou k jetons selon coût). Si le
seau est vide, la requête est rejetée immédiatement (HTTP 429,
Retry-After). Le calcul à l'arrivée d'une requête à
l'instant t : tokens = min(N, tokens_prev + (t -
t_prev) × R), puis tokens -= 1 si autorisé.
Algorithme O(1), seulement deux floats à stocker par compteur.
Implémentations : Bucket4j (Java), Cloudflare Edge Workers,
Kong, NGINX limit_req_zone, AWS API Gateway.
Recharge automatique
R = 100 jetons/sec
│
▼
┌──────────────┐
│ Seau N=500 │ capacité max (burst)
│ ●●●●●●●●●● │ jetons actuellement
│ ●●●●●●●●●● │
└──────────────┘
│ consume 1 par requête
▼
requête partenaire
│
si seau vide :
┌──────────────────────────┐
│ HTTP 429 Too Many Req │
│ Retry-After: N │
└──────────────────────────┘
Implémentation EDI
Cas concret : l'API REST d'un hub EDI accepte des INVOIC d'un
partenaire Walmart. Le contrat dit : 100 INVOIC/seconde
soutenu, burst 500. Sur Kong (API Gateway), plugin rate-limiting-advanced en mode sliding window ou un module Lua token-bucket :
limit=500, rate=100/s, identifier=consumer.partner_id.
Walmart envoie un batch de 480 messages à 14h32:00 : tous
passent (seau de 500 jetons, 480 consommés). À 14h32:01, il en
reste 20 + 100 rechargés = 120, etc. Si Walmart pousse 600 à 14h32:00,
les 100 derniers reçoivent HTTP 429 Retry-After: 1.
Côté Kafka producer : quota.producer.byte.rate
par client.id, équivalent token bucket sur octets. Côté
PEPPOL : norme OpenPEPPOL impose un débit max par Access
Point — typiquement implémenté en token bucket.
Paramètres pratiques
- R (refill rate) : la moyenne soutenue souhaitée par seconde. Doit correspondre à la capacité de l'aval.
- N (bucket size) : combien on tolère en burst. Souvent N = 5×R à 10×R.
- Identifier : par
partnerId, parapiKey, par IP. Choisir la clé du contrat. - Storage : Redis cluster pour le partage
multi-instance (avec
SCRIPTLua pour atomicité), mémoire locale si single-node. - Response on reject : HTTP 429,
Retry-After: N(RFC 6585), headerX-RateLimit-Remaining.
Anti-patterns
- Token bucket sans persistance. Restart du service → seau réinitialisé → burst libre.
- Token bucket multi-instance sans coordination. Chaque instance a son seau : le partenaire peut dépasser k×R en attaquant k instances. Centraliser via Redis.
- Burst N trop grand. Si N=10000 pour R=100, le pattern n'a aucun effet — le burst max sature toujours.
- R indépendant des partenaires. Un seul rate limit global pour tous : un partenaire bavard sature le quota de tous.
Patterns liés
- Rate Limiter — le pattern parent.
- Back-pressure — le rate limiter est une back-pressure contractuelle aval.
- Bulkhead — couplé : un token bucket par compartiment.
- Circuit Breaker — complémentaire : si l'aval échoue, ne pas consommer les jetons.
Sources
- RFC 2697 — A Single Rate Three Color Marker (Heinanen J., Guerin R., IETF 1999). Spec canonique du token bucket. rfc-editor.org/rfc/rfc2697
- RFC 2698 — A Two Rate Three Color Marker (1999). Variante avec deux taux (CIR/PIR). rfc-editor.org/rfc/rfc2698
- RFC 6585 §4 — HTTP 429 Too Many Requests (Nottingham M., IETF 2012). rfc-editor.org/rfc/rfc6585
- Stripe — Scaling your API with rate limiters. Retour d'expérience sur les rate limiters production. stripe.com/blog/rate-limiters
- Bucket4j — implémentation Java de référence du token bucket. bucket4j.com
- Cloudflare — How we built rate limiting. blog.cloudflare.com