Quand votre API commence à suffoquer : Introduction au rate limiting
Imaginez la situation : vous avez écrit un serveur parfait en C++. Rapide, efficace, il utilise tous les cœurs du processeur. Vous le déployez en production, et... cinq minutes plus tard, il plante. Que s'est-il passé ? Il s'avère qu'un petit script malicieux a décidé de « tester » votre API, en envoyant des milliers de requêtes par seconde.
Cela vous semble familier ? Si ce n'est pas le cas, vous avez de la chance. Mais tôt ou tard, chaque développeur est confronté à la nécessité de contrôler la charge. C'est là qu'interviennent le rate limiting et le throttling — deux mécanismes puissants qui sauvent les serveurs des surcharges et assurent une répartition uniforme des ressources.
Dans cet article, nous allons voir comment implémenter ces mécanismes en C++. Vous découvrirez non seulement la théorie, mais aussi du code fonctionnel que vous pourrez utiliser immédiatement dans vos projets. C'est parti !
Rate Limiting vs Throttling : Quelle est la différence ?
Avant de plonger dans le code, distinguons clairement ces deux concepts. Beaucoup les confondent, mais la différence existe et elle est cruciale.
Rate Limiting (Limitation de débit)
C'est une « barrière ». Vous dites : « Pas plus de 10 requêtes par seconde. Tout ce qui dépasse est banni (ou mis en file d'attente) ». Le rate limiting protège votre API contre les requêtes en avalanche, les attaques DDoS et les pics de trafic aléatoires.
Throttling (Étranglement)
C'est un « régulateur de volume ». Vous ne bloquez pas complètement les requêtes, mais vous ralentissez leur traitement. Par exemple, si un client envoie 100 requêtes par seconde, vous les traitez, mais avec un délai de 10 ms entre chacune. Le throttling est une manière plus « polie » de gérer la charge, il ne rejette pas les données, mais lisse simplement les pics.
Dans les projets réels, ces techniques sont souvent combinées : d'abord le throttling pour lisser, puis en cas de dépassement d'un seuil critique, le rate limiting avec retour d'une erreur 429 (Too Many Requests).
Algorithmes de rate limiting en C++
Il existe plusieurs algorithmes classiques. Nous allons voir les deux plus populaires et efficaces : Token Bucket et Sliding Window. Tous deux sont faciles à implémenter en C++ et donnent d'excellents résultats.
1. Token Bucket (Seau de jetons)
Imaginez un seau dans lequel N jetons tombent chaque seconde. Chaque requête prend un jeton. Si le seau est vide, la requête est rejetée. Simple et génial.
Voici une implémentation en C++ moderne utilisant std::chrono :
#include <chrono>#include <mutex>#include <atomic>
class TokenBucket {public: TokenBucket(size_t max_tokens, double refill_rate) : max_tokens_(max_tokens) , refill_rate_(refill_rate) , current_tokens_(max_tokens) , last_refill_(std::chrono::steady_clock::now()) {}
bool try_consume() { std::lock_guard<std::mutex> lock(mutex_); // Calcul du nombre de jetons accumulés depuis la dernière fois auto now = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_refill_); // Remplissage du seau double tokens_to_add = elapsed.count() * refill_rate_ / 1000.0; current_tokens_ = std::min(max_tokens_, current_tokens_ + tokens_to_add); last_refill_ = now; // Tentative de prise d'un jeton if (current_tokens_ > 0) { --current_tokens_; return true; } return false; }
private: std::mutex mutex_; size_t max_tokens_; // Capacité maximale du seau double refill_rate_; // Jetons par seconde double current_tokens_; // Nombre actuel de jetons std::chrono::steady_clock::time_point last_refill_;};Utilisation :
TokenBucket limiter(10, 1.0); // 10 jetons, remplissage 1 jeton/s
// Dans le gestionnaire de requête :if (limiter.tr