Aller au contenu

limit-traffic: bibliothèque Lua pour limiter et contrôler le trafic dans nginx-module-lua

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

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-limit-traffic v0.9 publié le 08 août 2023.


## démontrer l'utilisation du module resty.limit.req (seul !)
http {
    lua_shared_dict my_limit_req_store 100m;

    server {
        location / {
            access_by_lua_block {
                -- eh bien, nous pourrions mettre les appels require() et new() dans nos propres modules Lua
                -- pour économiser des ressources. ici nous les mettons ci-dessous juste pour
                -- commodité.

                local limit_req = require "resty.limit.req"

                -- limiter les requêtes à moins de 200 req/sec avec un pic de 100 req/sec,
                -- c'est-à-dire que nous retardons les requêtes à moins de 300 req/sec et au-dessus de 200
                -- req/sec, et rejetons toutes les requêtes dépassant 300 req/sec.
                local lim, err = limit_req.new("my_limit_req_store", 200, 100)
                if not lim then
                    ngx.log(ngx.ERR,
                            "échec de l'instanciation d'un objet resty.limit.req : ", 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)
                if not delay then
                    if err == "rejected" then
                        return ngx.exit(503)
                    end
                    ngx.log(ngx.ERR, "échec de la limitation des requêtes : ", err)
                    return ngx.exit(500)
                end

                if delay >= 0.001 then
                    -- la 2ème valeur de retour contient le nombre de requêtes excessives
                    -- par seconde pour la clé spécifiée. par exemple, le nombre 31
                    -- signifie que le taux de requêtes actuel est de 231 req/sec pour la
                    -- clé spécifiée.
                    local excess = err

                    -- la requête dépassant les 200 req/sec mais en dessous de 300 req/sec,
                    -- donc nous la retardons intentionnellement ici un peu pour respecter le
                    -- taux de 200 req/sec.
                    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.
        }
    }
}
## démontrer l'utilisation du module resty.limit.conn (seul !)
http {
    lua_shared_dict my_limit_conn_store 100m;

    server {
        location / {
            access_by_lua_block {
                -- eh bien, nous pourrions mettre les appels require() et new() dans nos propres modules Lua
                -- pour économiser des ressources. ici nous les mettons ci-dessous juste pour
                -- commodité.

                local limit_conn = require "resty.limit.conn"

                -- limiter les requêtes à moins de 200 requêtes concurrentes (normalement juste
                -- des connexions entrantes à moins que des protocoles comme SPDY ne soient utilisés) avec
                -- un pic de 100 requêtes concurrentes supplémentaires, c'est-à-dire que nous retardons
                -- les requêtes à moins de 300 connexions concurrentes et au-dessus de 200
                -- connexions, et rejetons toute nouvelle requête dépassant 300
                -- connexions.
                -- de plus, nous supposons un temps de requête par défaut de 0,5 sec, qui peut être
                -- ajusté dynamiquement par l'appel leaving() dans log_by_lua ci-dessous.
                local lim, err = limit_conn.new("my_limit_conn_store", 200, 100, 0.5)
                if not lim then
                    ngx.log(ngx.ERR,
                            "échec de l'instanciation d'un objet resty.limit.conn : ", 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)
                if not delay then
                    if err == "rejected" then
                        return ngx.exit(503)
                    end
                    ngx.log(ngx.ERR, "échec de la limitation des requêtes : ", 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

                -- la 2ème valeur de retour contient le niveau de concurrence actuel
                -- pour la clé spécifiée.
                local conn = err

                if delay >= 0.001 then
                    -- la requête dépassant le ratio de 200 connexions mais en dessous
                    -- de 300 connexions, donc
                    -- nous la retardons intentionnellement ici un peu pour respecter le
                    -- limite de 200 connexions.
                    -- ngx.log(ngx.WARN, "retard")
                    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.

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- si vous utilisez un module en amont dans la phase de contenu,
                    -- alors vous voudrez probablement utiliser $upstream_response_time
                    -- au lieu de ($request_time - ctx.limit_conn_delay) ci-dessous.
                    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,
                                "échec de l'enregistrement de la connexion sortante ",
                                "requête : ", err)
                        return
                    end
                end
            }
        }
    }
}
## démontrer l'utilisation du module 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, "échec de la limitation du trafic : ", 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("dormir ", delay, " sec, états : ",
                      table.concat(states, ", "))

                if delay >= 0.001 then
                    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.

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- si vous utilisez un module en amont dans la phase de contenu,
                    -- alors vous voudrez probablement utiliser $upstream_response_time
                    -- au lieu de $request_time ci-dessous.
                    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,
                                "échec de l'enregistrement de la connexion sortante ",
                                "requête : ", err)
                        return
                    end
                end
            }
        }
    }
}

Description

Cette bibliothèque fournit plusieurs modules Lua pour aider les utilisateurs d'OpenResty/ngx_lua à contrôler et limiter le trafic, soit le taux de requêtes soit la concurrence des requêtes (ou les deux).

Veuillez consulter la documentation de ces modules Lua pour plus de détails.

Cette bibliothèque fournit des alternatives plus flexibles aux modules standard de NGINX ngx_limit_req et ngx_limit_conn. Par exemple, les limiteurs basés sur Lua fournis par cette bibliothèque peuvent être utilisés dans n'importe quel contexte comme juste avant la procédure de négociation SSL en aval (comme avec ssl_certificate_by_lua) ou juste avant d'émettre des requêtes en arrière-plan.

nginx.conf

http { ... }

et ensuite charger l'un des modules fournis par cette bibliothèque en Lua. Par exemple,

```lua
local limit_req = require "resty.limit.req"

Voir aussi

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