Warum Redis Streams und Lua? Das dynamische Duo erklärt
Bevor wir uns in den Code stürzen, lassen Sie uns klären, warum diese Kombination das Batman und Robin der Ratenbegrenzung ist:
- Redis Streams: Stellen Sie sich das als eine leistungsstarke Nachrichtenwarteschlange mit Zeitreise-Fähigkeiten vor.
- Lua-Skripte: Das Multitool von Redis, das es Ihnen ermöglicht, komplexe Logik atomar auszuführen.
Zusammen sind sie wie Erdnussbutter und Marmelade, wenn Erdnussbutter Millionen von Anfragen pro Sekunde verarbeiten könnte und Marmelade atomare Operationen ausführen könnte. Lecker.
Der Plan: Unseren eigenen Ratenbegrenzer bauen
Hier ist der Plan:
- Verwenden Sie Redis Streams, um eingehende Anfragen zu protokollieren.
- Implementieren Sie einen gleitenden Fensteralgorithmus mit Lua-Skripten.
- Fügen Sie etwas Würze mit dynamischen Ratenanpassungen basierend auf der Serverlast hinzu.
Schritt 1: Anfragen mit Redis Streams protokollieren
Zuerst richten wir unseren Stream ein, um die eingehenden Anfragen zu protokollieren:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
def log_request(user_id):
return r.xadd('requests', {'user_id': user_id})
Einfach, oder? Wir fügen einfach jede Anfrage zu einem Stream namens 'requests' hinzu. Die Schönheit von Streams ist, dass sie zeitlich geordnet sind, was perfekt für unseren gleitenden Fensteransatz ist.
Schritt 2: Das Lua-Skript - Wo die Magie passiert
Jetzt schreiben wir ein Lua-Skript, das:
- Die Anzahl der Anfragen in den letzten X Sekunden überprüft
- Entscheidet, ob die Anfrage erlaubt sein soll
- Alte Einträge bereinigt
local function check_rate_limit(user_id, max_requests, window_size_ms)
local now = redis.call('TIME')
local now_ms = tonumber(now[1]) * 1000 + tonumber(now[2] / 1000)
local window_start = now_ms - window_size_ms
-- Alte Einträge entfernen
redis.call('XTRIM', 'requests', 'MINID', tostring(window_start))
-- Anfragen im Fenster zählen
local count = redis.call('XLEN', 'requests')
if count < max_requests then
-- Anfrage erlauben
redis.call('XADD', 'requests', '*', 'user_id', user_id)
return 1
else
-- Ratenbegrenzung überschritten
return 0
end
end
return check_rate_limit(KEYS[1], tonumber(ARGV[1]), tonumber(ARGV[2]))
Dieses Skript leistet viel:
- Es berechnet die aktuelle Zeit in Millisekunden
- Trimmt den Stream, um nur aktuelle Einträge zu behalten
- Zählt die Anfragen im aktuellen Fenster
- Entscheidet, ob die Anfrage erlaubt wird
Schritt 3: Alles zusammenfügen
Jetzt packen wir das in eine Python-Funktion:
lua_script = """
-- Unser Lua-Skript von oben kommt hierher
"""
rate_limiter = r.register_script(lua_script)
def is_allowed(user_id, max_requests=100, window_size_ms=60000):
return bool(rate_limiter(keys=[user_id], args=[max_requests, window_size_ms]))
# Verwendung
if is_allowed('user123'):
print("Anfrage erlaubt!")
else:
print("Ratenbegrenzung überschritten!")
Nächste Stufe: Dynamische Ratenanpassungen
Aber Moment, da ist noch mehr! Was wäre, wenn wir unsere Ratenbegrenzung basierend auf der Serverlast anpassen könnten? Fügen wir unserem Lua-Skript eine Wendung hinzu:
-- Fügen Sie dies oben in unser Lua-Skript ein
local server_load = tonumber(redis.call('GET', 'server_load') or "50")
local dynamic_max_requests = math.floor(max_requests * (100 - server_load) / 100)
-- Verwenden Sie dann dynamic_max_requests anstelle von max_requests in unserer Logik
Jetzt passen wir unsere Ratenbegrenzung basierend auf einem in Redis gespeicherten 'server_load'-Wert an. Sie könnten diesen Wert regelmäßig basierend auf Ihren tatsächlichen Servermetriken aktualisieren.
Die Fallstricke: Was könnte schiefgehen?
Bevor Sie dies in der Produktion implementieren, lassen Sie uns über einige potenzielle Probleme sprechen:
- Speichernutzung: Streams können viel Speicher verbrauchen, wenn sie nicht richtig getrimmt werden. Behalten Sie die Speichernutzung von Redis im Auge.
- Uhrabweichung: Wenn Sie dies auf mehreren Servern ausführen, stellen Sie sicher, dass deren Uhren synchronisiert sind.
- Lua-Skript-Komplexität: Denken Sie daran, dass Lua-Skripte Redis blockieren. Halten Sie sie kurz und prägnant.
Zusammenfassung: Warum das wichtig ist
Warum all diese Mühe, wenn Sie einfach eine vorgefertigte Lösung verwenden könnten? Hier ist der Grund:
- Flexibilität: Sie können dies an jedes noch so ausgefallene Ratenbegrenzungsschema anpassen, das Sie sich ausdenken können.
- Leistung: Diese Einrichtung kann eine enorme Anzahl von Anfragen pro Sekunde verarbeiten.
- Lernen: Der Aufbau von Grund auf gibt Ihnen ein tiefes Verständnis der Konzepte der Ratenbegrenzung.
Außerdem, seien wir ehrlich, es ist einfach cool zu sagen, dass Sie Ihren eigenen Ratenbegrenzer gebaut haben.
Denkanstoß
"Der einzige Weg, großartige Arbeit zu leisten, ist, zu lieben, was man tut." - Steve Jobs
Während wir diese Reise in die benutzerdefinierte Ratenbegrenzung abschließen, fragen Sie sich: Welche anderen "Standard"-Komponenten könnten von einer benutzerdefinierten, Redis-gestützten Überarbeitung profitieren? Die Möglichkeiten sind endlos, nur begrenzt durch Ihre Vorstellungskraft (und vielleicht den Speicher Ihrer Redis-Instanz).
Gehen Sie nun los und begrenzen Sie die Raten mit Stil! Ihre APIs werden es Ihnen danken, und wer weiß, vielleicht sind Sie das Gesprächsthema beim nächsten Entwickler-Treffen. "Oh, Sie verwenden einen vorgefertigten Ratenbegrenzer? Das ist süß."