Wenn Ihre API zu ersticken beginnt: Einführung in Rate Limiting
Stellen Sie sich folgende Situation vor: Sie haben einen perfekten Server in C++ geschrieben. Er ist schnell, effizient und nutzt alle Prozessorkerne. Sie bringen ihn in die Produktion und... nach fünf Minuten stürzt er ab. Was ist passiert? Es stellte sich heraus, dass ein skriptgesteuerter Rowdy Ihre API „testen“ wollte, indem er Tausende von Anfragen pro Sekunde sendete.
Kommt Ihnen das bekannt vor? Wenn nicht, haben Sie Glück gehabt. Aber früher oder später steht jeder Entwickler vor der Notwendigkeit, die Last zu kontrollieren. Genau hier kommen Rate Limiting und Throttling ins Spiel – zwei leistungsstarke Mechanismen, die Server vor Überlastung schützen und eine gleichmäßige Ressourcenverteilung gewährleisten.
In diesem Artikel werden wir uns ansehen, wie man diese Mechanismen in C++ implementiert. Sie lernen nicht nur die Theorie, sondern erhalten auch funktionierenden Code, den Sie sofort in Ihren Projekten verwenden können. Los geht's!
Rate Limiting vs. Throttling: Was ist der Unterschied?
Bevor wir in den Code eintauchen, wollen wir diese beiden Konzepte klar voneinander abgrenzen. Viele verwechseln sie, aber es gibt einen Unterschied, und dieser ist entscheidend.
Rate Limiting (Geschwindigkeitsbegrenzung)
Dies ist eine „Schranke“. Sie sagen: „In einer Sekunde – nicht mehr als 10 Anfragen. Alles, was darüber hinausgeht, wird gesperrt (oder in die Warteschlange gestellt).“ Rate Limiting schützt Ihre API vor lawinenartigen Anfragen, DDoS-Angriffen und zufälligen Traffic-Spitzen.
Throttling (Drosselung)
Dies ist ein „Lautstärkeregler“. Sie blockieren Anfragen nicht vollständig, sondern verlangsamen deren Verarbeitung. Wenn ein Client beispielsweise 100 Anfragen pro Sekunde sendet, verarbeiten Sie diese, jedoch mit einer Verzögerung von 10 ms zwischen jeder. Throttling ist eine „höflichere“ Methode zur Laststeuerung, die Daten nicht verwirft, sondern lediglich Spitzen glättet.
In realen Projekten werden diese Techniken oft kombiniert: zuerst Throttling zur Glättung, und bei Überschreitung einer kritischen Schwelle dann Rate Limiting mit Rückgabe des Fehlers 429 (Too Many Requests).
Rate-Limiting-Algorithmen in C++
Es gibt mehrere klassische Algorithmen. Wir werden uns die beiden beliebtesten und effektivsten ansehen: Token Bucket und Sliding Window. Beide lassen sich leicht in C++ implementieren und liefern hervorragende Ergebnisse.
1. Token Bucket (Token-Eimer)
Stellen Sie sich einen Eimer vor, in den jede Sekunde N Token tropfen. Jede Anfrage nimmt einen Token. Ist der Eimer leer, wird die Anfrage abgelehnt. Einfach und genial.
Hier ist eine Implementierung in modernem C++ unter Verwendung von 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_); // Berechnen, wie viele Token seit dem letzten Mal hinzugekommen sind auto now = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_refill_); // Eimer auffüllen double tokens_to_add = elapsed.count() * refill_rate_ / 1000.0; current_tokens_ = std::min(max_tokens_, current_tokens_ + tokens_to_add); last_refill_ = now; // Versuchen, einen Token zu nehmen if (current_tokens_ > 0) { --current_tokens_; return true; } return false; }
private: std::mutex mutex_; size_t max_tokens_; // Maximale Kapazität des Eimers double refill_rate_; // Token pro Sekunde double current_tokens_; // Aktuelle Anzahl der Token std::chrono::steady_clock::time_point last_refill_;};Verwendung:
TokenBucket limiter(10, 1.0); // 10 Token, Nachfüllrate 1 Token/Sek.
// Im Anfrage-Handler:if (limiter.tr