Перейти к содержанию

limit-traffic: Lua библиотека для ограничения и контроля трафика в nginx-module-lua

Установка

Если вы еще не настроили подписку на репозиторий RPM, подпишитесь. Затем вы можете продолжить с следующими шагами.

CentOS/RHEL 7 или 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

Чтобы использовать эту Lua библиотеку с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-limit-traffic v0.9, выпущенную 8 августа 2023 года.


## демонстрация использования модуля resty.limit.req (в одиночку!)
http {
    lua_shared_dict my_limit_req_store 100m;

    server {
        location / {
            access_by_lua_block {
                -- ну, мы могли бы поместить вызовы require() и new() в наши собственные Lua
                -- модули, чтобы сэкономить накладные расходы. здесь мы помещаем их ниже просто для
                -- удобства.

                local limit_req = require "resty.limit.req"

                -- ограничить запросы до 200 req/sec с всплеском в 100 req/sec,
                -- то есть мы задерживаем запросы ниже 300 req/sec и выше 200
                -- req/sec и отклоняем любые запросы, превышающие 300 req/sec.
                local lim, err = limit_req.new("my_limit_req_store", 200, 100)
                if not lim then
                    ngx.log(ngx.ERR,
                            "не удалось создать объект resty.limit.req: ", err)
                    return ngx.exit(500)
                end

                -- следующий вызов должен быть на каждый запрос.
                -- здесь мы используем удаленный (IP) адрес в качестве ключа ограничения
                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, "не удалось ограничить запрос: ", err)
                    return ngx.exit(500)
                end

                if delay >= 0.001 then
                    -- 2 возвращаемый параметр содержит количество избыточных запросов
                    -- в секунду для указанного ключа. например, число 31
                    -- означает, что текущая скорость запросов составляет 231 req/sec для
                    -- указанного ключа.
                    local excess = err

                    -- запрос превышает 200 req/sec, но ниже 300 req/sec,
                    -- поэтому мы намеренно задерживаем его здесь немного, чтобы соответствовать
                    -- скорости 200 req/sec.
                    ngx.sleep(delay)
                end
            }

            # обработчик контента идет сюда. если это content_by_lua, то вы можете
            # объединить Lua код выше в access_by_lua в ваш обработчик Lua content_by_lua,
            # чтобы сэкономить немного времени ЦП.
        }
    }
}
## демонстрация использования модуля resty.limit.conn (в одиночку!)
http {
    lua_shared_dict my_limit_conn_store 100m;

    server {
        location / {
            access_by_lua_block {
                -- ну, мы могли бы поместить вызовы require() и new() в наши собственные Lua
                -- модули, чтобы сэкономить накладные расходы. здесь мы помещаем их ниже просто для
                -- удобства.

                local limit_conn = require "resty.limit.conn"

                -- ограничить запросы до 200 одновременных запросов (обычно это просто
                -- входящие соединения, если не используются такие протоколы, как SPDY) с
                -- всплеском в 100 дополнительных одновременных запросов, то есть мы задерживаем
                -- запросы ниже 300 одновременных соединений и выше 200
                -- соединений и отклоняем любые новые запросы, превышающие 300
                -- соединений.
                -- также мы предполагаем стандартное время запроса 0.5 сек, которое может быть
                -- динамически скорректировано вызовом leaving() в log_by_lua ниже.
                local lim, err = limit_conn.new("my_limit_conn_store", 200, 100, 0.5)
                if not lim then
                    ngx.log(ngx.ERR,
                            "не удалось создать объект resty.limit.conn: ", err)
                    return ngx.exit(500)
                end

                -- следующий вызов должен быть на каждый запрос.
                -- здесь мы используем удаленный (IP) адрес в качестве ключа ограничения
                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, "не удалось ограничить запрос: ", 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

                -- 2 возвращаемый параметр содержит текущий уровень одновременности
                -- для указанного ключа.
                local conn = err

                if delay >= 0.001 then
                    -- запрос превышает соотношение 200 соединений, но ниже
                    -- 300 соединений, поэтому
                    -- мы намеренно задерживаем его здесь немного, чтобы соответствовать
                    -- лимиту в 200 соединений.
                    -- ngx.log(ngx.WARN, "задержка")
                    ngx.sleep(delay)
                end
            }

            # обработчик контента идет сюда. если это content_by_lua, то вы можете
            # объединить Lua код выше в access_by_lua в ваш
            # обработчик Lua content_by_lua, чтобы сэкономить немного времени ЦП.

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- если вы используете модуль upstream в фазе контента,
                    -- то вам, вероятно, нужно использовать $upstream_response_time
                    -- вместо ($request_time - ctx.limit_conn_delay) ниже.
                    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,
                                "не удалось зафиксировать выход соединения ",
                                "запроса: ", err)
                        return
                    end
                end
            }
        }
    }
}
## демонстрация использования модуля 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, "не удалось ограничить трафик: ", 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("задержка ", delay, " сек, состояния: ",
                      table.concat(states, ", "))

                if delay >= 0.001 then
                    ngx.sleep(delay)
                end
            }

            # обработчик контента идет сюда. если это content_by_lua, то вы можете
            # объединить Lua код выше в access_by_lua в ваш
            # обработчик Lua content_by_lua, чтобы сэкономить немного времени ЦП.

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- если вы используете модуль upstream в фазе контента,
                    -- то вам, вероятно, нужно использовать $upstream_response_time
                    -- вместо $request_time ниже.
                    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,
                                "не удалось зафиксировать выход соединения ",
                                "запроса: ", err)
                        return
                    end
                end
            }
        }
    }
}

Описание

Эта библиотека предоставляет несколько Lua модулей, чтобы помочь пользователям OpenResty/ngx_lua контролировать и ограничивать трафик, как скорость запросов, так и одновременность запросов (или то и другое).

  • resty.limit.req предоставляет ограничение скорости запросов и корректировку на основе метода "протекающего ведра".
  • resty.limit.count предоставляет ограничение скорости на основе реализации "фиксированного окна" с версии OpenResty 1.13.6.1+.
  • resty.limit.conn предоставляет ограничение уровня одновременности запросов и корректировку на основе дополнительных задержек.
  • resty.limit.traffic предоставляет агрегатор для объединения нескольких экземпляров классов resty.limit.req, resty.limit.count или resty.limit.conn (или всех).

Пожалуйста, ознакомьтесь с документацией этих Lua модулей для получения более подробной информации.

Эта библиотека предоставляет более гибкие альтернативы стандартным модулям NGINX ngx_limit_req и ngx_limit_conn. Например, ограничители на основе Lua, предоставленные этой библиотекой, могут использоваться в любых контекстах, например, прямо перед процедурой SSL-рукопожатия (как с ssl_certificate_by_lua) или прямо перед выдачей запросов к бэкенду.

nginx.conf

http { ... }

и затем загрузите один из модулей, предоставляемых этой библиотекой, в Lua. Например,

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

См. также

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-limit-traffic.