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

redis-ratelimit: Ограничение скорости обработки запросов между несколькими экземплярами NGINX, использующими Redis

Установка

Если вы еще не подписались на репозиторий 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-redis-ratelimit

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-redis-ratelimit

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

Этот документ описывает lua-resty-redis-ratelimit v0.3, выпущенный 3 октября 2019 года.


Эта библиотека lua является модулем ограничения скорости обработки запросов для ngx_lua:

http://wiki.nginx.org/HttpLuaModule

Она используется для ограничения скорости обработки запросов по определенному ключу между несколькими экземплярами NGINX. Ограничение осуществляется с использованием метода "leaky bucket".

Этот модуль использует Redis (>= 2.6.0) в качестве бэкенд-хранилища, поэтому вам также нужна библиотека lua-resty-redis для работы с ней.

ВНИМАНИЕ: Если вы не используете функцию duration и входящий трафик равномерно распределен, рекомендуется использовать модуль resty.limit.req, чтобы избежать ненужных задержек в сети.

Синопсис

server {

    listen 9090;

    location /t {
        access_by_lua_block {
            local ratelimit = require "resty.redis.ratelimit"

            local lim, err = ratelimit.new("one", "2r/s", 0, 2)
            if not lim then
                ngx.log(ngx.ERR,
                        "не удалось создать объект resty.redis.ratelimit: ", err)
                return ngx.exit(500)
            end

            -- ВНИМАНИЕ: следующий вызов должен выполняться для каждого запроса.

            -- local redis = require "resty.redis"
            -- local red = redis:new()

            -- red:set_timeout(1000)

            -- local ok, err = red:connect("127.0.0.1", 6379)
            -- if not ok then
            --     ngx.log(ngx.ERR, "не удалось подключиться к redis: ", err)
            --     return ngx.exit(500)
            -- end

            local red = { host = "127.0.0.1", port = 6379, timeout = 1 }

            local key = ngx.var.binary_remote_addr
            local delay, err = lim:incoming(key, red)
            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
                -- второе возвращаемое значение содержит количество избыточных запросов
                -- в секунду для указанного ключа.
                local excess = err

                ngx.sleep(delay)
            end
        '}

        echo Logged in;
    }

}

Методы

new

синтаксис: obj, err = class.new(zone, rate, burst, duration)

Создает объект этого класса. Значение класса возвращается вызовом require resty.redis.ratelimit.

Этот метод принимает следующие аргументы:

  • zone: Устанавливает пространство имен, в частности, мы используем строку <zone>:<key> в качестве уникального идентификатора состояния внутри Redis.
  • rate: Скорость указывается в запросах в секунду (r/s). Если требуется скорость менее одного запроса в секунду, она указывается в запросах в минуту (r/m). Например, ползапроса в секунду — это 30r/m.
  • burst: Определяет, сколько запросов можно сделать сверх скорости, указанной в зоне, по умолчанию 0.
  • duration: Время задержки (в секундах) перед возвратом к нормальному состоянию, в течение этого периода запрос всегда rejected, по умолчанию 0.

В случае ошибки этот метод возвращает nil и строку, описывающую ошибку.

incoming

синтаксис: delay, err = obj:incoming(key, redis)

Запускает новое событие входящего запроса и вычисляет необходимую задержку (если такая есть) для текущего запроса по указанному ключу или решает, следует ли немедленно его отклонить.

Этот метод принимает следующие аргументы:

  • key: Ключ — это любое непустое значение указанной переменной.
  • redis: Устанавливает конфигурацию Redis, host, port, timeout и так далее (см. ниже); Вместо конкретной конфигурации Redis вы также можете установить подключенный объект Redis напрямую.
- redis.host: По умолчанию 127.0.0.1.
- redis.port: По умолчанию 80.
- redis.timeout: По умолчанию 1s.
- redis.pass: Запрос для аутентификации на сервере Redis с защитой паролем.
- redis.dbid: Выбор логической базы данных Redis.

Возвращаемые значения зависят от следующих случаев:

  1. Если запрос не превышает значение rate, указанное в методе new, то этот метод возвращает 0 как задержку и (ноль) количество избыточных запросов в секунду в текущий момент.
  2. Если запрос превышает лимит rate, указанный в методе new, но не значение rate + burst, тогда этот метод возвращает соответствующую задержку (в секундах) для текущего запроса, чтобы он все еще соответствовал порогу rate, как если бы он пришел немного позже, а не сейчас. Второе возвращаемое значение указывает количество избыточных запросов в секунду в этот момент (включая текущий запрос).
  3. Если запрос превышает лимит rate + burst, тогда этот метод возвращает nil и строку ошибки "rejected".
  4. Если произошла ошибка, тогда этот метод возвращает nil и строку, описывающую ошибку. Например, "не удалось создать redis - соединение отклонено".

Этот метод никогда не спит сам по себе. Он просто возвращает задержку, если это необходимо, и требует от вызывающего позже вызвать метод ngx.sleep для ожидания.

set_burst

синтаксис: obj:set_burst(burst)

Перезаписывает порог burst, как указано в методе new.

См. также

GitHub

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