Rate Limiting in Java: So schützen Sie Ihre API vor Überlastung und Bots

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

Rate Limiting und Throttling in Java: Von der Theorie zum produktiven Code



Stellen Sie sich vor: Ihr neuer Microservice auf Java steht gerade erst, als plötzlich eine Lawine von Anfragen auf ihn einprasselt. Keine Angreifer, nein – nur ein nachlässiger Kunde, der vergessen hat, eine Verzögerung in sein Skript einzubauen. Das Ergebnis: Die Datenbank fällt aus, der Server kämpft, und die Benutzer (einschließlich der gewissenhaften) erhalten einen 502 Bad Gateway. Kommt Ihnen das bekannt vor?



Heute sprechen wir darüber, wie Sie das vermeiden können. Unser Thema ist Rate Limiting und Throttling in Java. Dies sind nicht nur modische Begriffe aus der DevOps-Welt, sondern Ihre erste Verteidigungslinie für jede öffentliche API. Wir werden erläutern, wie sich „Geschwindigkeitsbegrenzung“ von „Drosselung“ unterscheidet, uns gängige Algorithmen ansehen und, am wichtigsten, echten Code schreiben.



Wenn Sie in Java entwickeln und möchten, dass Ihr Dienst unter jeder Last am Leben bleibt, sind Sie hier genau richtig. Los geht's!



Rate Limiting vs Throttling: Was ist der Unterschied?



Oft werden diese Begriffe verwechselt, obwohl sie unterschiedliche, wenn auch verwandte, Probleme lösen.



  • Rate Limiting ist eine harte Grenze. „Nicht mehr als 10 Anfragen pro Sekunde von einer IP“. Limit überschritten? Sie erhalten 429 Too Many Requests. Ganz einfach.
  • Throttling ist ein weicherer Mechanismus. Der Dienst blockiert die Anfrage nicht, sondern verlangsamt ihre Verarbeitung. Sie stellen die Anfrage beispielsweise in eine Warteschlange und verarbeiten sie mit einer festgelegten Geschwindigkeit.


In realen Projekten wird Throttling in Java oft über Thread-Pools (ThreadPoolExecutor) mit begrenztem Durchsatz implementiert, während Rate Limiting über Zähler, Caches und Zwischenfilter realisiert wird.



Rate Limiting Algorithmen: Die Wahl der Waffe



Bevor wir Code schreiben, wählen wir eine Strategie. Hier sind die drei beliebtesten Algorithmen für die Anfragenbegrenzung in Java.



1. Token Bucket (Token-Eimer)

Ein Klassiker. Wir haben einen „Eimer“, in den jede Sekunde N neue Token tropfen. Um eine Anfrage auszuführen, muss ein Token entnommen werden. Ist der Eimer leer, wird die Anfrage abgelehnt. Der Algorithmus kommt gut mit plötzlichen Verkehrsspitzen zurecht, da er erlaubt, Token im Voraus zu „leihen“ (bis zur Eimergröße).



2. Sliding Window (Gleitendes Fenster)

Eine genauere Methode. Anstatt Anfragen pro Kalendersekunde zu zählen, verschieben wir das Zeitfenster. Zum Beispiel: „Nicht mehr als 100 Anfragen in den letzten 60 Sekunden“. Dies verhindert einen „Ansturm“ zu Beginn jedes Intervalls.



3. Fixed Window (Festes Fenster)

Die einfachste, aber gefährlichste Methode. Der Zähler wird alle N Sekunden zurückgesetzt. Problem: Am Ende eines Fensters können tausend Anfragen gesendet werden und zu Beginn des nächsten weitere tausend. Für kritische Systeme ungeeignet.



Wir konzentrieren uns auf die ersten beiden – sie bieten die beste Balance zwischen Leistung und Genauigkeit.



Code schreiben: Rate Limiter selbst gebaut



Lassen Sie uns einen Rate Limiter in Java mit dem Token Bucket Algorithmus implementieren. Dies ist ein hervorragendes Beispiel, um zu verstehen, wie Begrenzungssysteme intern funktionieren.



import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicLong;

public class TokenBucketRateLimiter {

private final long maxTokens; // Maximale Eimergröße private final long refillRate; // Token pro Sekunde private final ConcurrentHashMap<String, Bucket> buckets = new ConcurrentHashMap<>();

public TokenBucketRateLimiter(long maxTokens, long refillRate) { this.maxTokens = maxTokens; this.refillRate = refillRate; }

public boolean tryAcquire(String clientId) { Bucket bucket = buckets.computeIfAbsent(clientId, k -> new Bucket(maxTokens)); return bucket.tryConsume(); }

private class Bucket { private final AtomicLong tokens; private volatile long lastRefillTimestamp;

Bucket(long cap

Blogs

Buchempfehlungen