limit-rate: module Lua pour limiter le taux de requêtes pour nginx-module-lua, utilisant la méthode "token bucket"
Installation
Si vous n'avez pas encore configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.
CentOS/RHEL 7 ou 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
Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.
Ce document décrit lua-resty-limit-rate v0.1 publié le 25 octobre 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, -- utiliser lua-resty-lock
locks_shdict_name = "my_locks",
})
if not lim then
ngx.log(ngx.ERR,
"échec de l'instanciation d'un objet resty.limit.rate : ", err)
return ngx.exit(500)
end
-- l'appel suivant doit être par requête.
-- ici nous utilisons l'adresse (IP) distante comme clé de limitation
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, "échec de la prise de token : ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
-- la 2ème valeur de retour contient le nombre actuel de tokens disponibles
-- de requêtes pour la clé spécifiée
local avail = err
ngx.sleep(delay)
end
}
# le gestionnaire de contenu va ici. s'il s'agit de content_by_lua, alors vous pouvez
# fusionner le code Lua ci-dessus dans access_by_lua dans le gestionnaire Lua de votre content_by_lua
# pour économiser un peu de temps CPU.
}
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, "échec de la prise globale : ", err)
return ngx.exit(500)
end
-- ici nous utilisons l'identifiant utilisateur comme clé de limitation
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, "échec de la prise unique : ", err)
return ngx.exit(500)
end
if t0 == 1 then
return -- le seau global n'est pas affamé
else
if t1 == 1 then
return -- le seau unique n'est pas affamé
else
return ngx.exit(503)
end
end
}
}
}
}
Description
Ce module fournit des API pour aider les programmeurs utilisateurs d'OpenResty/ngx_lua à limiter le taux de requêtes en utilisant la méthode "token bucket".
Si vous souhaitez utiliser plusieurs instances différentes de cette classe en même temps ou utiliser une instance de cette classe avec des instances d'autres classes (comme resty.limit.conn), alors vous devez utiliser le module resty.limit.traffic pour les combiner.
La principale différence entre ce module et resty.limit.req :
- resty.limit.req limite le taux de requêtes en utilisant la méthode "leaky bucket", ce module utilise la méthode "token bucket".
La principale différence entre ce module et resty.limit.count :
- resty.limit.count offre un modèle mental simple qui limite le taux de requêtes par un nombre fixe de requêtes dans une fenêtre de temps donnée, mais il peut parfois laisser passer deux fois le nombre de requêtes autorisées par minute. Par exemple, si notre limite de taux était de 10 requêtes par minute et qu'un utilisateur faisait 10 requêtes à 10:00:59, il pourrait faire 10 autres requêtes à 10:01:00 car un nouveau compteur commence au début de chaque minute. Dans ce cas, ce module est capable de contrôler plus précisément et en douceur.
Méthodes
new
syntax: obj, err = class.new(shdict_name, interval, capacity, quantum?, max_wait?, opts?)
Instancie un objet de cette classe. La valeur class est renvoyée par l'appel require "resty.limit.rate".
La méthode renvoie un nouveau seau de tokens qui se remplit au taux de quantum tokens tous les interval, jusqu'à la capacité maximale donnée capacity. Le seau est initialement plein.
Cette méthode prend les arguments suivants et une table d'options facultative opts :
-
shdict_nameest le nom de la zone shm lua_shared_dict.Il est recommandé d'utiliser des zones shm séparées pour différents types de limiteurs.
-
intervalest le temps écoulé entre l'ajout de tokens, en millisecondes. -
capacityest le nombre maximum de tokens à conserver dans le seau. -
quantumest le nombre de tokens à ajouter au seau dans un intervalle, cet argument est facultatif, par défaut1. -
max_waitest le temps maximum que nous attendrions pour que suffisamment de tokens soient ajoutés, en millisecondes, cet argument est facultatif, par défautnil, ce qui signifie l'infini.
La table d'options accepte les options suivantes :
-
lock_enableLorsqu'il est activé, la mise à jour de l'état shdict à travers plusieurs processus de travail nginx est atomique ; sinon, il y aura une fenêtre de condition de course (petite) entre le comportement "lire puis écrire", par défautfalse. Voir lua-resty-lock pour plus de détails. -
locks_shdict_nameSpécifie le nom du dictionnaire partagé (créé par lua_shared_dict) pour le verrou, par défautlocks.
En cas d'échec, cette méthode renvoie nil et une chaîne décrivant l'erreur (comme un mauvais nom de lua_shared_dict).
incoming
syntax: delay, err = obj:take(key, commit)
Déclenche un nouvel événement de requête entrante et calcule le délai nécessaire (le cas échéant) pour la requête actuelle sur la clé spécifiée ou si l'utilisateur doit la rejeter immédiatement.
Semblable à la méthode take, mais cette méthode ne prend qu'un seul token du seau à la fois.
Cette méthode accepte les arguments suivants :
-
keyest la clé spécifiée par l'utilisateur pour limiter le taux.Veuillez noter que ce module ne préfixe ni ne suffixe la clé utilisateur, il est donc de la responsabilité de l'utilisateur de s'assurer que la clé est unique dans la zone shm
lua_shared_dict. -
commitest une valeur booléenne. Si elle est définie surtrue, l'objet enregistrera réellement l'événement dans la zone shm soutenant l'objet actuel ; sinon, ce ne serait qu'un "essai à sec" (ce qui est la valeur par défaut).
set_max_wait
syntax: obj:set_max_wait(max_wait?)
Écrase le seuil max_wait tel que spécifié dans la méthode new.
take
syntax: delay, err = obj:take(key, count, commit)
La méthode prend count tokens du seau sans bloquer.
Cette méthode accepte les arguments suivants :
-
keyest la clé spécifiée par l'utilisateur pour limiter le taux.Veuillez noter que ce module ne préfixe ni ne suffixe la clé utilisateur, il est donc de la responsabilité de l'utilisateur de s'assurer que la clé est unique dans la zone shm
lua_shared_dict. -
countest le nombre de tokens à retirer. -
commitest une valeur booléenne. Si elle est définie surtrue, l'objet enregistrera réellement l'événement dans la zone shm soutenant l'objet actuel ; sinon, ce ne serait qu'un "essai à sec" (ce qui est la valeur par défaut).
Les valeurs de retour dépendent des cas suivants :
-
Si la valeur
max_waitspécifiée dans la méthode new ou set_max_wait, la méthode ne prendra des tokens du seau que si le temps d'attente pour les tokens n'est pas supérieur àmax_wait, et renvoie le temps que l'appelant doit attendre jusqu'à ce que les tokens soient réellement disponibles, sinon elle renvoienilet la chaîne d'erreur"rejected". -
Si la valeur
max_waitest nil, elle renvoie le temps que l'appelant doit attendre jusqu'à ce que les tokens soient réellement disponibles.
De plus, cette méthode renvoie également une deuxième valeur de retour indiquant le nombre de tokens disponibles à ce moment-là.
Si une erreur s'est produite (comme des échecs lors de l'accès à la zone shm lua_shared_dict soutenant l'objet actuel), alors cette méthode renvoie nil et une chaîne décrivant l'erreur.
Cette méthode ne dort jamais elle-même. Elle renvoie simplement un délai si nécessaire et nécessite que l'appelant invoque plus tard la méthode ngx.sleep pour dormir.
take_available
syntax: count, err = obj:take_available(key, count)
La méthode prend jusqu'à count tokens immédiatement disponibles du seau. Elle renvoie le nombre de tokens retirés, ou zéro s'il n'y a pas de tokens disponibles. Elle ne bloque pas.
Cette méthode accepte les arguments suivants :
-
keyest la clé spécifiée par l'utilisateur pour limiter le taux.Veuillez noter que ce module ne préfixe ni ne suffixe la clé utilisateur, il est donc de la responsabilité de l'utilisateur de s'assurer que la clé est unique dans la zone shm
lua_shared_dict. -
countest le nombre de tokens à retirer.
Si une erreur s'est produite (comme des échecs lors de l'accès à la zone shm lua_shared_dict soutenant l'objet actuel), alors cette méthode renvoie nil et une chaîne décrivant l'erreur.
uncommit
syntax: ok, err = obj:uncommit(key)
Cela essaie d'annuler l'engagement de l'appel incoming. C'est simplement une approximation et doit être utilisé avec précaution. Cette méthode est principalement destinée à être utilisée dans le module Lua resty.limit.traffic lors de la combinaison de plusieurs limiteurs en même temps.
Granularité de Limitation
La limitation fonctionne sur la granularité d'une instance de serveur NGINX individuelle (y compris tous ses processus de travail). Grâce au mécanisme shm, nous pouvons partager l'état de manière économique entre tous les travailleurs dans une seule instance de serveur NGINX.
Voir Aussi
- module resty.limit.req
- module resty.limit.conn
- module resty.limit.count
- module resty.limit.traffic
- bibliothèque lua-resty-limit-traffic
- le module ngx_lua : https://github.com/openresty/lua-nginx-module
- OpenResty : https://openresty.org/
GitHub
Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-limit-rate.