Rate Limiting und Throttling in Kotlin: So schützen Sie Ihre API vor Überlastung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Wenn Ihre API "SOS" schreit: Einführung in Rate Limiting und Throttling

\n\n

Stellen Sie sich vor: Sie haben eine perfekte Kotlin-Anwendung geschrieben. Sie ist schnell, elegant, nutzt Koroutinen und Flow. Sie haben sie in Produktion genommen, und dann – bumm! – ein "freundlicher" Benutzer (oder schlimmer noch, ein Angreifer) beginnt, Ihren Endpunkt mit Tausenden von Anfragen pro Sekunde zu bombardieren. Der Server stürzt ab, die Datenbank weint und die anderen Benutzer erhalten 503 Service Unavailable.

\n\n

Kommt Ihnen das bekannt vor? Genau dafür gibt es die Mechanismen Rate Limiting (Ratenbegrenzung) und Throttling (Drosselung). In diesem Artikel werden wir nicht nur über die Theorie sprechen, sondern anhand praktischer Beispiele erläutern, wie Sie diese Muster in Kotlin implementieren.

\n\n

Wir werden Code schreiben, der Ihr Backend wirklich schützt, egal ob es sich um eine Anwendung mit Ktor, Spring Boot oder eine reine Koroutine handelt. Los geht's!

\n\n

Rate Limiting vs. Throttling: Was ist der Unterschied (und warum ist er wichtig)?

\n\n

Beginnen wir mit den Grundlagen, um nicht in den Begriffen durcheinanderzukommen. Obwohl diese Konzepte oft synonym verwendet werden, gibt es einen feinen Unterschied.

\n\n
    \n
  • Rate Limiting ist eine harte Grenze. "Du kannst nur 100 Anfragen pro Minute machen. Nach der 101. erhältst du 429 Too Many Requests und bist bis zur nächsten Minute gesperrt." Es geht um die Grenze.
  • \n
  • Throttling ist eine weichere Regulierung. "Du kannst keine Anfragen schneller als einmal alle 200 ms senden. Wenn du schneller sendest, stellen wir sie in eine Warteschlange oder ignorieren sie einfach." Es geht um die Geschwindigkeit.
  • \n
\n\n

In der Welt von Kotlin implementieren wir meistens Rate Limiting auf API-Ebene (Middleware) und Throttling auf der Ebene bestimmter Operationen (z. B. um einen externen Server nicht mit Anfragen zu überhäufen).

\n\n

Algorithmen, die jeder Kotlin-Entwickler kennen sollte

\n\n

Bevor wir Code schreiben, wählen wir ein Werkzeug aus. Es gibt drei klassische Algorithmen, die sich perfekt in die Kotlin-Architektur einfügen.

\n\n

1. Token Bucket (Eimer mit Token) – Der Beliebteste

\n

Stellen Sie sich einen Eimer vor, in den jede Sekunde N neue Token tropfen. Der Eimer hat ein maximales Volumen (Burst). Um eine Anfrage zu verarbeiten, muss ein Token entnommen werden. Wenn keine Token vorhanden sind, wird die Anfrage abgelehnt.

\n

Vorteile: Erlaubt kleine Lastspitzen (Bursts), einfach zu implementieren.

\n\n

2. Leaky Bucket (Undichter Eimer) – Glättung von Spitzen

\n

Ein Eimer mit einem Loch. Anfragen gelangen in den Eimer, und das "Loch" gibt sie mit konstanter Geschwindigkeit wieder ab. Wenn der Eimer überläuft, werden Anfragen verworfen.

\n

Vorteile: Gleicht die Last perfekt aus, erzeugt keine Spitzen.

\n\n

3. Sliding Window (Gleitendes Fenster) – Präzision ohne Ruckeln

\n

Anstelle fester Grenzen (wie bei "1 Minute") betrachten wir ein gleitendes Zeitfenster. Schwieriger zu implementieren, aber genauer in der Abbildung der tatsächlichen Last.

\n\n

Für Kotlin-Projekte ist Token Bucket der goldene Mittelweg zwischen Einfachheit und Effizienz. Diesen werden wir implementieren.

\n\n

Code schreiben: Rate Limiter in Kotlin von Grund auf (ohne Bibliotheken)

\n\n

Lassen Sie uns einen einfachen, aber funktionsfähigen Rate Limiter erstellen, der Koroutinen und Mutex verwendet. Dies wird eine threadsichere Version des Token Bucket sein.

\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, // Token pro Sekunde\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

Buchempfehlungen