python / expert
Snippet
Middleware zur Ratenbegrenzung mit gleitendem Fenster via atomare Redis Lua-Skripte
Diese Django-Middleware implementiert einen präzisen Ratenbegrenzer mit gleitendem Fenster (Sliding Window) in Redis. Das atomare Lua-Skript verhindert Race Conditions bei der Berechnung dynamischer Abfragen im Zeitfenster.
snippet.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import timefrom django.core.cache import cachefrom django.http import JsonResponseSLIDING_WINDOW_LUA = """local key = KEYS[1]local now = tonumber(ARGV[1])local clearBefore = now - tonumber(ARGV[2])local limit = tonumber(ARGV[3])redis.call('ZREMRANGEBYSCORE', key, 0, clearBefore)local currentRequests = redis.call('ZCARD', key)if currentRequests < limit thenredis.call('ZADD', key, now, now)redis.call('EXPIRE', key, tonumber(ARGV[2]))return 1elsereturn 0end"""class SlidingWindowRateLimitMiddleware:def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):client_ip = request.META.get('REMOTE_ADDR', 'unknown')cache_key = f"ratelimit:{client_ip}"now = time.time()# 100 requests per 60 seconds limitallowed = cache.client.get_client().eval(SLIDING_WINDOW_LUA, 1, cache_key, now, 60, 100)if not allowed:return JsonResponse({"error": "Too many requests"}, status=429)return self.get_response(request)
django
Erklärung
1
local currentRequests = redis.call('ZCARD', key)
Verwendet Redis Sorted Sets, um Anfragen strikt innerhalb des aktiven gleitenden Zeitfensters zu zählen.
2
redis.call('ZREMRANGEBYSCORE', key, 0, clearBefore)
Löscht veraltete Zeitstempel, die außerhalb des Zeitfensters liegen, vor der Berechnungsprüfung.
3
allowed = cache.client.get_client().eval(...)
Führt das Lua-Skript atomar auf dem Redis-Client aus, um das Anfragenlimit ohne Thread-Locking zu prüfen.