Rate Limiting et Throttling en Kotlin : Comment protéger votre API des surcharges

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Quand votre API crie "SOS" : Introduction au Rate Limiting et au Throttling

\n\n

Imaginez : vous avez écrit une application Kotlin parfaite. Elle est rapide, élégante, utilise les coroutines et Flow. Vous l'avez mise en production, et là — bam ! — un "gentil" utilisateur (ou, pire, un attaquant) commence à bombarder votre endpoint avec des milliers de requêtes par seconde. Le serveur tombe, la base de données pleure, et les autres utilisateurs reçoivent un 503 Service Unavailable.

\n\n

Une douleur familière ? C'est exactement pour cela que les mécanismes de rate limiting (limitation de débit) et de throttling (étranglement) existent. Dans cet article, nous ne nous contenterons pas de parler de théorie, mais nous verrons, avec des exemples pratiques, comment implémenter ces modèles en Kotlin.

\n\n

Nous allons écrire du code qui protégera réellement votre backend, que ce soit une application avec Ktor, Spring Boot ou une simple coroutine. C'est parti !

\n\n

Rate Limiting vs Throttling : Quelle est la différence (et pourquoi est-ce important) ?

\n\n

Commençons par les bases pour ne pas confondre les termes. Bien que ces concepts soient souvent utilisés comme synonymes, il y a une nuance entre eux.

\n\n
    \n
  • Rate Limiting — c'est une limite stricte. "Tu ne peux faire que 100 requêtes par minute. Après la 101e — tu obtiendras un 429 Too Many Requests et un bannissement jusqu'à la minute suivante". C'est une question de limite.
  • \n
  • Throttling — c'est une régulation plus douce. "Tu ne peux pas envoyer de requêtes plus vite qu'une toutes les 200 ms. Si tu envoies plus vite — nous les mettrons en file d'attente ou les ignorerons simplement". C'est une question de vitesse.
  • \n
\n\n

Dans le monde du Kotlin, nous implémentons le plus souvent le rate limiting au niveau de l'API (middleware) et le throttling au niveau d'opérations spécifiques (par exemple, pour ne pas spammer un serveur externe avec des requêtes).

\n\n

Les algorithmes que tout développeur Kotlin devrait connaître

\n\n

Avant d'écrire du code, choisissons notre outil. Il existe trois algorithmes classiques qui s'intègrent parfaitement à l'architecture Kotlin.

\n\n

1. Token Bucket (Seau de jetons) — Le plus populaire

\n

Imaginez un seau dans lequel N nouveaux jetons tombent chaque seconde. Le seau a un volume maximum (burst). Pour traiter une requête, il faut prendre un jeton. S'il n'y a pas de jetons, la requête est rejetée.

\n

Avantages : Permet de petites pointes de charge (bursts), simple à implémenter.

\n\n

2. Leaky Bucket (Seau percé) — Lissage des pics

\n

Un seau avec un trou. Les requêtes entrent dans le seau, et le "trou" les libère à une vitesse constante. Si le seau déborde, les requêtes sont rejetées.

\n

Avantages : Équilibre parfaitement la charge, ne crée pas de pics.

\n\n

3. Sliding Window (Fenêtre glissante) — Précision sans à-coups

\n

Au lieu de limites rigides (comme dans "1 minute"), nous examinons une période de temps glissante. Plus complexe à implémenter, mais reflète plus précisément la charge réelle.

\n\n

Pour les projets Kotlin, le Token Bucket est le juste milieu entre simplicité et efficacité. C'est celui que nous allons implémenter.

\n\n

Écrivons le code : Rate Limiter en Kotlin from scratch (sans bibliothèques)

\n\n

Créons un Rate Limiter simple mais fonctionnel, en utilisant les coroutines et Mutex. Ce sera une version thread-safe du Token Bucket.

\n\n
import kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlin.time.Duration\nimport kotlin.time.TimeSource\n\nclass TokenBucketRateLimiter(\n    private val maxTokens: Int,\n    private val refillRate: Long, // tokens par seconde\n    private val timeSource: TimeSource = TimeSource.Monotonic\n) {\n    private var tokens: Long = maxTokens.toLong()\n    private var lastRefillTimestamp: Long = timeSource.markNow().elapsedNow().inWholeMilliseconds\n    private val mutex = Mutex()\n\n    suspend fun tryAcquire(): Boolean {\n        mutex.withLock {\n            refillTokens()\n            return if (tokens > 0) {\n                tokens--\n                true\n            } else {\n                

Blogs

Book Recommendations