Saltar a contenido

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  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  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).

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

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.