limit-traffic: biblioteca Lua para limitar y controlar el tráfico en nginx-module-lua
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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-traffic
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-traffic
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-limit-traffic v0.9 lanzado el 08 de agosto de 2023.
## demostrar el uso del módulo resty.limit.req (¡solo!)
http {
lua_shared_dict my_limit_req_store 100m;
server {
location / {
access_by_lua_block {
-- bueno, podríamos poner las llamadas require() y new() en nuestros propios módulos Lua
-- para ahorrar sobrecarga. aquí las ponemos abajo solo por
-- conveniencia.
local limit_req = require "resty.limit.req"
-- limitar las solicitudes a menos de 200 req/sec con un pico de 100 req/sec,
-- es decir, retrasamos las solicitudes a menos de 300 req/sec y más de 200
-- req/sec, y rechazamos cualquier solicitud que exceda 300 req/sec.
local lim, err = limit_req.new("my_limit_req_store", 200, 100)
if not lim then
ngx.log(ngx.ERR,
"falló al instanciar un objeto resty.limit.req: ", err)
return ngx.exit(500)
end
-- la siguiente llamada debe ser por solicitud.
-- aquí usamos la dirección remota (IP) como la clave limitante
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "falló al limitar req: ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
-- el 2º valor de retorno contiene el número de solicitudes excesivas
-- por segundo para la clave especificada. por ejemplo, el número 31
-- significa que la tasa de solicitud actual es de 231 req/sec para la
-- clave especificada.
local excess = err
-- la solicitud excede las 200 req/sec pero está por debajo de 300 req/sec,
-- así que la retrasamos intencionadamente aquí un poco para ajustarnos a la
-- tasa de 200 req/sec.
ngx.sleep(delay)
end
}
# el manejador de contenido va aquí. si es content_by_lua, entonces puedes
# fusionar el código Lua anterior en access_by_lua en el manejador Lua de tu
# content_by_lua para ahorrar un poco de tiempo de CPU.
}
}
}
## demostrar el uso del módulo resty.limit.conn (¡solo!)
http {
lua_shared_dict my_limit_conn_store 100m;
server {
location / {
access_by_lua_block {
-- bueno, podríamos poner las llamadas require() y new() en nuestros propios módulos Lua
-- para ahorrar sobrecarga. aquí las ponemos abajo solo por
-- conveniencia.
local limit_conn = require "resty.limit.conn"
-- limitar las solicitudes a menos de 200 solicitudes concurrentes (normalmente solo
-- conexiones entrantes a menos que se utilicen protocolos como SPDY) con
-- un pico de 100 solicitudes concurrentes adicionales, es decir, retrasamos
-- las solicitudes a menos de 300 conexiones concurrentes y más de 200
-- conexiones, y rechazamos cualquier nueva solicitud que exceda 300
-- conexiones.
-- además, asumimos un tiempo de solicitud predeterminado de 0.5 seg, que puede ser
-- ajustado dinámicamente por la llamada leaving() en log_by_lua a continuación.
local lim, err = limit_conn.new("my_limit_conn_store", 200, 100, 0.5)
if not lim then
ngx.log(ngx.ERR,
"falló al instanciar un objeto resty.limit.conn: ", err)
return ngx.exit(500)
end
-- la siguiente llamada debe ser por solicitud.
-- aquí usamos la dirección remota (IP) como la clave limitante
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "falló al limitar req: ", err)
return ngx.exit(500)
end
if lim:is_committed() then
local ctx = ngx.ctx
ctx.limit_conn = lim
ctx.limit_conn_key = key
ctx.limit_conn_delay = delay
end
-- el 2º valor de retorno contiene el nivel de concurrencia actual
-- para la clave especificada.
local conn = err
if delay >= 0.001 then
-- la solicitud excede la proporción de 200 conexiones pero está por debajo
-- de 300 conexiones, así que
-- la retrasamos intencionadamente aquí un poco para ajustarnos a la
-- limitación de 200 conexiones.
-- ngx.log(ngx.WARN, "retrasando")
ngx.sleep(delay)
end
}
# el manejador de contenido va aquí. si es content_by_lua, entonces puedes
# fusionar el código Lua anterior en access_by_lua en el manejador Lua de tu
# content_by_lua para ahorrar un poco de tiempo de CPU.
log_by_lua_block {
local ctx = ngx.ctx
local lim = ctx.limit_conn
if lim then
-- si estás usando un módulo upstream en la fase de contenido,
-- entonces probablemente quieras usar $upstream_response_time
-- en lugar de ($request_time - ctx.limit_conn_delay) a continuación.
local latency = tonumber(ngx.var.request_time) - ctx.limit_conn_delay
local key = ctx.limit_conn_key
assert(key)
local conn, err = lim:leaving(key, latency)
if not conn then
ngx.log(ngx.ERR,
"falló al registrar la conexión saliente ",
"solicitud: ", err)
return
end
end
}
}
}
}
## demostrar el uso del módulo resty.limit.traffic
http {
lua_shared_dict my_req_store 100m;
lua_shared_dict my_conn_store 100m;
server {
location / {
access_by_lua_block {
local limit_conn = require "resty.limit.conn"
local limit_req = require "resty.limit.req"
local limit_traffic = require "resty.limit.traffic"
local lim1, err = limit_req.new("my_req_store", 300, 200)
assert(lim1, err)
local lim2, err = limit_req.new("my_req_store", 200, 100)
assert(lim2, err)
local lim3, err = limit_conn.new("my_conn_store", 1000, 1000, 0.5)
assert(lim3, err)
local limiters = {lim1, lim2, lim3}
local host = ngx.var.host
local client = ngx.var.binary_remote_addr
local keys = {host, client, client}
local states = {}
local delay, err = limit_traffic.combine(limiters, keys, states)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "falló al limitar el tráfico: ", err)
return ngx.exit(500)
end
if lim3:is_committed() then
local ctx = ngx.ctx
ctx.limit_conn = lim3
ctx.limit_conn_key = keys[3]
end
print("durmiendo ", delay, " seg, estados: ",
table.concat(states, ", "))
if delay >= 0.001 then
ngx.sleep(delay)
end
}
# el manejador de contenido va aquí. si es content_by_lua, entonces puedes
# fusionar el código Lua anterior en access_by_lua en el manejador Lua de tu
# content_by_lua para ahorrar un poco de tiempo de CPU.
log_by_lua_block {
local ctx = ngx.ctx
local lim = ctx.limit_conn
if lim then
-- si estás usando un módulo upstream en la fase de contenido,
-- entonces probablemente quieras usar $upstream_response_time
-- en lugar de $request_time a continuación.
local latency = tonumber(ngx.var.request_time)
local key = ctx.limit_conn_key
assert(key)
local conn, err = lim:leaving(key, latency)
if not conn then
ngx.log(ngx.ERR,
"falló al registrar la conexión saliente ",
"solicitud: ", err)
return
end
end
}
}
}
}
Descripción
Esta biblioteca proporciona varios módulos Lua para ayudar a los usuarios de OpenResty/ngx_lua a controlar y limitar el tráfico, ya sea la tasa de solicitudes o la concurrencia de solicitudes (o ambos).
- resty.limit.req proporciona limitación de tasa de solicitudes y ajuste basado en el método "bucket con fugas".
- resty.limit.count proporciona limitación de tasa basada en una implementación de "ventana fija" desde OpenResty 1.13.6.1+.
- resty.limit.conn proporciona limitación del nivel de concurrencia de solicitudes y ajuste basado en retrasos adicionales.
- resty.limit.traffic proporciona un agregador para combinar múltiples instancias de las clases resty.limit.req, resty.limit.count o resty.limit.conn (o todas).
Por favor, consulta la documentación de estos módulos Lua para más detalles.
Esta biblioteca proporciona alternativas más flexibles a los módulos estándar de NGINX
ngx_limit_req
y ngx_limit_conn.
Por ejemplo, los limitadores basados en Lua proporcionados por esta biblioteca pueden ser utilizados en cualquier contexto
como justo antes del procedimiento de apretón de manos SSL downstream (como con ssl_certificate_by_lua)
o justo antes de emitir solicitudes al backend.
nginx.conf
http { ... }
y luego carga uno de los módulos proporcionados por esta biblioteca en Lua. Por ejemplo,
```lua
local limit_req = require "resty.limit.req"
Véase También
- módulo resty.limit.req
- módulo resty.limit.count
- módulo resty.limit.conn
- módulo resty.limit.traffic
- el módulo ngx_lua: https://github.com/openresty/lua-nginx-module
- OpenResty: https://openresty.org/
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-limit-traffic.