limit-rate: Lua-Modul zur Begrenzung der Anforderungsrate für nginx-module-lua, unter Verwendung der "Token-Bucket"-Methode
Installation
Wenn Sie noch kein RPM-Repository-Abonnement eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.
CentOS/RHEL 7 oder Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-limit-rate
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-limit-rate
Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.
Dieses Dokument beschreibt lua-resty-limit-rate v0.1, veröffentlicht am 25. Oktober 2018.
http {
lua_shared_dict my_limit_rate_store 100m;
lua_shared_dict my_locks 100k;
server {
location / {
access_by_lua_block {
local limit_rate = require "resty.limit.rate"
local lim, err = limit_rate.new("my_limit_rate_store", 500, 10, 3, 200, {
lock_enable = true, -- verwende lua-resty-lock
locks_shdict_name = "my_locks",
})
if not lim then
ngx.log(ngx.ERR,
"Fehler beim Instanziieren eines resty.limit.rate-Objekts: ", err)
return ngx.exit(500)
end
-- der folgende Aufruf muss pro Anfrage erfolgen.
-- hier verwenden wir die Remote-(IP)-Adresse als das begrenzende Schlüssel
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
-- local delay, err = lim:take(key, 1, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "Fehler beim Entnehmen des Tokens: ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
-- der 2. Rückgabewert enthält die aktuelle Anzahl verfügbarer Tokens
-- von Anfragen für den angegebenen Schlüssel
local avail = err
ngx.sleep(delay)
end
}
# Inhaltshandler kommt hierhin. Wenn es content_by_lua ist, können Sie
# den obigen Lua-Code in access_by_lua in den Lua-Handler Ihres content_by_lua
# integrieren, um ein wenig CPU-Zeit zu sparen.
}
location /take_available {
access_by_lua_block {
local limit_rate = require "resty.limit.rate"
-- global 20r/s 6000r/5m
local lim_global = limit_rate.new("my_limit_rate_store", 100, 6000, 2, nil, {
lock_enable = true,
locks_shdict_name = "my_locks",
})
if not lim_global then
return ngx.exit(500)
end
-- single 2r/s 600r/5m
local lim_single = limit_rate.new("my_limit_rate_store", 500, 600, 1, nil, {
locks_shdict_name = "my_locks",
})
if not lim_single then
return ngx.exit(500)
end
local t0, err = lim_global:take_available("__global__", 1)
if not t0 then
ngx.log(ngx.ERR, "Fehler beim Entnehmen von global: ", err)
return ngx.exit(500)
end
-- hier verwenden wir die Benutzer-ID als den begrenzenden Schlüssel
local key = ngx.var.arg_userid or "__single__"
local t1, err = lim_single:take_available(key, 1)
if not t1 then
ngx.log(ngx.ERR, "Fehler beim Entnehmen von single: ", err)
return ngx.exit(500)
end
if t0 == 1 then
return -- globaler Bucket ist nicht hungrig
else
if t1 == 1 then
return -- einzelner Bucket ist nicht hungrig
else
return ngx.exit(503)
end
end
}
}
}
}
Beschreibung
Dieses Modul bietet APIs, um Benutzern von OpenResty/ngx_lua zu helfen, die Anforderungsrate mithilfe der "Token-Bucket"-Methode zu begrenzen.
Wenn Sie mehrere verschiedene Instanzen dieser Klasse gleichzeitig verwenden oder eine Instanz dieser Klasse mit Instanzen anderer Klassen (wie resty.limit.conn) verwenden möchten, müssen Sie das resty.limit.traffic Modul verwenden, um sie zu kombinieren.
Der Hauptunterschied zwischen diesem Modul und resty.limit.req:
- resty.limit.req begrenzt die Anforderungsrate mithilfe der "leaky bucket"-Methode, während dieses Modul die "Token-Bucket"-Methode verwendet.
Der Hauptunterschied zwischen diesem Modul und resty.limit.count:
- resty.limit.count bietet ein einfaches mentales Modell, das die Anforderungsrate durch eine feste Anzahl von Anfragen in einem bestimmten Zeitfenster begrenzt, aber manchmal kann es die doppelte Anzahl der erlaubten Anfragen pro Minute durchlassen. Zum Beispiel, wenn unsere Ratenbegrenzung 10 Anfragen pro Minute beträgt und ein Benutzer um 10:00:59 10 Anfragen stellt, könnte er um 10:01:00 10 weitere Anfragen stellen, da ein neuer Zähler zu Beginn jeder Minute beginnt. In diesem Fall kann dieses Modul präziser und reibungsloser steuern.
Methoden
new
Syntax: obj, err = class.new(shdict_name, interval, capacity, quantum?, max_wait?, opts?)
Instanziiert ein Objekt dieser Klasse. Der class-Wert wird durch den Aufruf require "resty.limit.rate" zurückgegeben.
Die Methode gibt einen neuen Token-Bucket zurück, der mit einer Rate von quantum Tokens alle interval füllt, bis zur angegebenen maximalen capacity. Der Bucket ist anfangs voll.
Diese Methode nimmt die folgenden Argumente und eine optionale Options-Tabelle opts entgegen:
-
shdict_nameist der Name des lua_shared_dict shm-Bereichs.Es ist eine bewährte Methode, separate shm-Bereiche für verschiedene Arten von Limitierern zu verwenden.
-
intervalist die Zeitspanne zwischen dem Hinzufügen von Tokens in Millisekunden. -
capacityist die maximale Anzahl von Tokens, die im Bucket gehalten werden können. -
quantumist die Anzahl von Tokens, die in einem Intervall zum Bucket hinzugefügt werden, dieses Argument ist optional, Standardwert ist1. -
max_waitist die maximale Zeit, die wir warten würden, bis genügend Tokens hinzugefügt werden, in Millisekunden, dieses Argument ist optional, Standardwert istnil, was Unendlichkeit bedeutet.
Die Options-Tabelle akzeptiert die folgenden Optionen:
-
lock_enableWenn aktiviert, wird der Zustand des shm über mehrere nginx-Arbeitsprozesse hinweg atomar aktualisiert; andernfalls gibt es ein (kleines) Zeitfenster für Rennbedingungen zwischen dem Verhalten "lesen-und-dann-schreiben", Standardwert istfalse. Weitere Details finden Sie in lua-resty-lock. -
locks_shdict_nameGibt den Namen des gemeinsamen Wörterbuchs (erstellt durch lua_shared_dict) für das Lock an, Standardwert istlocks.
Im Fehlerfall gibt diese Methode nil und eine Zeichenfolge zurück, die den Fehler beschreibt (wie einen ungültigen lua_shared_dict-Namen).
incoming
Syntax: delay, err = obj:take(key, commit)
Löst ein neues Ereignis für eingehende Anfragen aus und berechnet die erforderliche Verzögerung (falls vorhanden) für die aktuelle Anfrage basierend auf dem angegebenen Schlüssel oder ob der Benutzer sie sofort ablehnen sollte.
Ähnlich wie die take Methode, aber diese Methode entnimmt nur ein Token aus dem Bucket auf einmal.
Diese Methode akzeptiert die folgenden Argumente:
-
keyist der vom Benutzer angegebene Schlüssel zur Begrenzung der Rate.Bitte beachten Sie, dass dieses Modul den Benutzer-Schlüssel weder vor noch nachanfügt, sodass es in der Verantwortung des Benutzers liegt, sicherzustellen, dass der Schlüssel im
lua_shared_dictshm-Bereich eindeutig ist. -
commitist ein boolescher Wert. Wenn auftruegesetzt, wird das Ereignis tatsächlich im shm-Bereich, der das aktuelle Objekt unterstützt, aufgezeichnet; andernfalls wäre es nur ein "Trockenlauf" (was der Standard ist).
set_max_wait
Syntax: obj:set_max_wait(max_wait?)
Überschreibt den max_wait-Schwellenwert, wie im new Methode angegeben.
take
Syntax: delay, err = obj:take(key, count, commit)
Die Methode entnimmt count Tokens aus dem Bucket, ohne zu blockieren.
Diese Methode akzeptiert die folgenden Argumente:
-
keyist der vom Benutzer angegebene Schlüssel zur Begrenzung der Rate.Bitte beachten Sie, dass dieses Modul den Benutzer-Schlüssel weder vor noch nachanfügt, sodass es in der Verantwortung des Benutzers liegt, sicherzustellen, dass der Schlüssel im
lua_shared_dictshm-Bereich eindeutig ist. -
countist die Anzahl der zu entnehmenden Tokens. -
commitist ein boolescher Wert. Wenn auftruegesetzt, wird das Ereignis tatsächlich im shm-Bereich, der das aktuelle Objekt unterstützt, aufgezeichnet; andernfalls wäre es nur ein "Trockenlauf" (was der Standard ist).
Die Rückgabewerte hängen von den folgenden Fällen ab:
-
Wenn der
max_wait-Wert, der in der new oder set_max_wait Methode angegeben ist, wird die Methode nur Tokens aus dem Bucket entnehmen, wenn die Wartezeit für die Tokens nicht größer alsmax_waitist, und gibt die Zeit zurück, die der Aufrufer warten sollte, bis die Tokens tatsächlich verfügbar sind, andernfalls gibt sienilund die Fehlermeldung"rejected"zurück. -
Wenn der
max_wait-Wertnilist, gibt er die Zeit zurück, die der Aufrufer warten sollte, bis die Tokens tatsächlich verfügbar sind.
Darüber hinaus gibt diese Methode auch einen zweiten Rückgabewert zurück, der die Anzahl der aktuell verfügbaren Tokens zu diesem Zeitpunkt angibt.
Wenn ein Fehler aufgetreten ist (wie z. B. Fehler beim Zugriff auf den lua_shared_dict shm-Bereich, der das aktuelle Objekt unterstützt), gibt diese Methode nil und eine Zeichenfolge zurück, die den Fehler beschreibt.
Diese Methode schläft niemals selbst. Sie gibt einfach eine Verzögerung zurück, falls erforderlich, und erfordert, dass der Aufrufer später die ngx.sleep Methode aufruft, um zu schlafen.
take_available
Syntax: count, err = obj:take_available(key, count)
Die Methode entnimmt bis zu count sofort verfügbaren Tokens aus dem Bucket. Sie gibt die Anzahl der entnommenen Tokens zurück oder null, wenn keine verfügbaren Tokens vorhanden sind. Sie blockiert nicht.
Diese Methode akzeptiert die folgenden Argumente:
-
keyist der vom Benutzer angegebene Schlüssel zur Begrenzung der Rate.Bitte beachten Sie, dass dieses Modul den Benutzer-Schlüssel weder vor noch nachanfügt, sodass es in der Verantwortung des Benutzers liegt, sicherzustellen, dass der Schlüssel im
lua_shared_dictshm-Bereich eindeutig ist. -
countist die Anzahl der zu entnehmenden Tokens.
Wenn ein Fehler aufgetreten ist (wie z. B. Fehler beim Zugriff auf den lua_shared_dict shm-Bereich, der das aktuelle Objekt unterstützt), gibt diese Methode nil und eine Zeichenfolge zurück, die den Fehler beschreibt.
uncommit
Syntax: ok, err = obj:uncommit(key)
Dies versucht, das Commit des incoming-Aufrufs rückgängig zu machen. Dies ist einfach eine Annäherung und sollte mit Vorsicht verwendet werden. Diese Methode ist hauptsächlich für die Verwendung im resty.limit.traffic Lua-Modul gedacht, wenn mehrere Limitierer gleichzeitig kombiniert werden.
Begrenzungsgranularität
Die Begrenzung funktioniert auf der Granularität einer einzelnen NGINX-Serverinstanz (einschließlich aller ihrer Arbeitsprozesse). Dank des shm-Mechanismus können wir den Zustand kostengünstig über alle Worker in einer einzelnen NGINX-Serverinstanz teilen.
Siehe auch
- Modul resty.limit.req
- Modul resty.limit.conn
- Modul resty.limit.count
- Modul resty.limit.traffic
- Bibliothek lua-resty-limit-traffic
- das ngx_lua Modul: https://github.com/openresty/lua-nginx-module
- OpenResty: https://openresty.org/
GitHub
Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-limit-rate.