# DRF Throttling vollständig verstehen: Warum es nötig ist und wie man es einrichtet, anwendet und anpasst Ein "limit_req" in einem nginx‑Reverse‑Proxy ist zweifellos effektiv, um den Eingang zu drosseln. Wenn man jedoch **unterschiedliche Richtlinien pro View/Action** (z. B. Login 5 mal pro Minute, Uploads 20 mal pro Tag, Such‑API 1 000 mal pro Benutzer) setzen möchte, ist es schwierig, sich ausschließlich auf Server‑ oder Infrastruktur‑Einstellungen zu verlassen. DRF‑Throttling ermöglicht es, **anwendungsspezifische Beschränkungen** zu definieren, die sich an den Eigenschaften der Endpunkte orientieren – ein unverzichtbares Grundwissen. --- ## Warum Throttling nötig ist DRF‑Throttling entscheidet, ob eine Anfrage **erlaubt** wird – ähnlich wie ein Permission‑Check – jedoch ist der Unterschied, dass es sich um eine **temporäre** Beschränkung handelt, nicht um eine dauerhafte Berechtigung. Die Dokumentation beschreibt Throttling als „eine temporäre Zustandskontrolle, die die Geschwindigkeit von API‑Anfragen eines Clients steuert“. Die praktischen Gründe lassen sich wie folgt zusammenfassen: * **Missbrauch/Angriffe reduzieren**: Brute‑Force‑Login, Spam‑Anfragen, Crawling, einfacher DoS * **Kosten/Resourcen schützen**: Uploads, externe API‑Aufrufe, schwere Abfragen, generative‑AI‑Calls * **Fairness**: Verhindern, dass ein einzelner Benutzer/Key Ressourcen monopolisiert * **Regeln im Code**: „Diese API darf pro Minute N‑mal“ – statt auf Infrastruktur‑Ebenen zu setzen Wichtig: Throttling kann **global** oder **pro View/Action** angewendet werden – ein Bereich, in dem nginx allein oft nicht ausreicht. --- ## Wie DRF Throttling funktioniert (nur Kernkonzepte) ### 1) Rate‑String DRF verwendet üblicherweise Formate wie `"100/day"` oder `"60/min"`. ### 2) Wer wird beschränkt? (Client‑Identifikation) * `UserRateThrottle`: Authentifizierte Benutzer werden nach **user‑id** und nicht‑authentifizierte nach **IP** beschränkt * `AnonRateThrottle`: Nur nicht‑authentifizierte Anfragen, basierend auf **IP** * `ScopedRateThrottle`: Richtlinien werden pro **Scope** (z. B. "uploads") definiert IP‑Erkennung erfolgt über `X-Forwarded-For` oder `REMOTE_ADDR`. Bei Proxy‑Umgebungen ist die Einstellung `NUM_PROXIES` entscheidend. ### 3) Zustandsspeicherung – Cache Die Standard‑Implementierung speichert Zähler im Django‑Cache‑Backend. Bei einer einzelnen Instanz reicht `LocMemCache`, aber bei mehreren Workern oder Replikaten ist ein gemeinsamer Cache wie Redis fast zwingend. --- ## Globale (Global) Konfiguration: Schnellstart `settings.py`: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "rest_framework.throttling.UserRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "anon": "100/day", "user": "1000/day", }, } ``` Damit gilt die **Standardrichtlinie** für alle Endpunkte. Bei Überschreitung sendet DRF standardmäßig **HTTP 429 (Too Many Requests)**. --- ## Throttling pro View: Unterschiedliche Beschränkungen pro Endpunkt ### 1) Klassische Views (APIView) ```python from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.throttling import UserRateThrottle class ExpensiveView(APIView): throttle_classes = [UserRateThrottle] def get(self, request): return Response({"ok": True}) ``` ### 2) Funktionsbasierte Views (@api_view) ```python from rest_framework.decorators import api_view, throttle_classes from rest_framework.throttling import UserRateThrottle from rest_framework.response import Response @api_view(["GET"]) @throttle_classes([UserRateThrottle]) def ping(request): return Response({"pong": True}) ``` ### 3) ViewSet‑Action‑spezifisch (@action) ```python from rest_framework.decorators import action from rest_framework.throttling import UserRateThrottle from rest_framework.viewsets import ViewSet from rest_framework.response import Response class ItemViewSet(ViewSet): @action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle]) def purchase(self, request, pk=None): return Response({"purchased": pk}) ``` Die Action‑Spezifikation hat Vorrang vor der ViewSet‑Einstellung. --- ## ScopedRateThrottle: Richtlinien pro View‑Eigenschaft (empfohlen) Scoped‑Throttling trennt Richtlinien nach sinnvollen Namen (z. B. "uploads", "login"). `settings.py`: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.ScopedRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "login": "5/min", "uploads": "20/day", "search": "60/min", }, } ``` View‑Deklaration: ```python from rest_framework.views import APIView from rest_framework.response import Response class LoginView(APIView): throttle_scope = "login" def post(self, request): return Response({"ok": True}) ``` DRF erstellt einen eindeutigen Schlüssel aus **Scope + user‑id oder IP**. --- ## Eigene Throttle‑Klasse erstellen: Der Schlüssel ist die Identifikation Selbst mit eingebauten Throttles gibt es häufig Anforderungen: * Login nach **IP + Username** beschränken * API‑Key‑basiertes Throttling * Beschränkung nach Header/Tenant/Organisation * Nutzung eines anderen Caches (z. B. Redis‑Cluster) ### 1) Häufigste Methode: SimpleRateThrottle erben ```python from rest_framework.throttling import SimpleRateThrottle class LoginBurstThrottle(SimpleRateThrottle): scope = "login" def get_cache_key(self, request, view): username = (request.data.get("username") or "").lower().strip() ident = self.get_ident(request) # IP‑basierte Identifikation if not username: return None # Kein Username → kein Throttle return f"throttle_login:{ident}:{username}" ``` Registrierung in `settings.py`: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "path.to.LoginBurstThrottle", ], "DEFAULT_THROTTLE_RATES": { "login": "5/min", }, } ``` ### 2) Cache‑Attribute ändern ```python from django.core.cache import caches from rest_framework.throttling import AnonRateThrottle class CustomCacheAnonThrottle(AnonRateThrottle): cache = caches["alternate"] ``` --- ## Vor dem Deployment: Wichtige Punkte ### 1) IP‑Erkennung hinter Proxies `X-Forwarded-For`/`REMOTE_ADDR` bestimmen die IP. Ohne korrekte `NUM_PROXIES`‑Einstellung werden alle Anfragen als ein einziger Client behandelt. ### 2) LocMemCache bei Multi‑Worker/Server Lokale Caches führen zu inkonsistenten Zählern. Für stabile Throttles ist ein gemeinsamer Cache wie Redis unerlässlich. ### 3) Race‑Conditions bei hoher Parallelität Die Standard‑Implementierung kann bei hohem Durchsatz zu kleinen Überschreitungen führen. Für kritische Fälle (z. B. Zahlungen) empfiehlt sich ein atomarer Zähler (Redis INCR + EXPIRE). ### 4) Client‑freundlich: 429 & Retry‑After DRF gibt bei Überschreitung 429 zurück. Durch Implementierung von `wait()` kann ein `Retry-After`‑Header gesendet werden. --- ## Fazit: nginx + DRF‑Throttling – beides nutzen ![Roboterclub‑Einstiegsschranke und 429‑Neonschild](/media/editor_temp/6/c96bb1d6-7cde-48a0-98aa-07396570391d.png) * **nginx**: Schützt vor massiver Traffic‑Spitzen und Angriffen an der Front‑End‑Schicht * **DRF‑Throttling**: Anwendungs‑Level‑Richtlinien, die Endpunkt‑Kosten und -Bedeutung berücksichtigen Besonders die **View‑spezifische Beschränkung** ist ein starkes Feature von DRF, das unabhängig von Infrastrukturänderungen im Code bleibt.