跳转至

limit-traffic: 用于限制和控制 nginx-module-lua 中流量的 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

要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua

本文档描述了 lua-resty-limit-traffic v0.9,于 2023 年 8 月 8 日发布。


## 演示 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 次请求,突发为每秒 100 次请求,
                -- 也就是说,我们延迟每秒 300 次请求和超过 200 次请求的请求,
                -- 并拒绝任何超过每秒 300 次请求的请求。
                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
                    -- 第二个返回值保存指定键的超额请求数
                    -- 每秒。例如,数字 31 表示当前请求速率为 231 次请求/秒
                    -- 对于指定的键。
                    local excess = err

                    -- 请求超过 200 次请求/秒但低于 300 次请求/秒,
                    -- 所以我们故意在这里稍微延迟一下以符合
                    -- 200 次请求/秒的速率。
                    ngx.sleep(delay)
                end
            }

            # 内容处理程序在这里。如果是 content_by_lua,那么您可以
            # 将上面的 Lua 代码合并到您的 content_by_lua 的
            # Lua 处理程序中,以节省一点 CPU 时间。
        }
    }
}
## 演示 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 个额外的并发请求,
                -- 也就是说,我们延迟超过 200 个连接和低于 300 个连接的请求,
                -- 并拒绝任何超过 300 个连接的新请求。
                -- 此外,我们假设默认请求时间为 0.5 秒,可以通过
                -- log_by_lua 中的 leaving() 调用动态调整。
                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

                -- 第二个返回值保存指定键的当前并发级别。
                local conn = err

                if delay >= 0.001 then
                    -- 请求超过 200 个连接比例但低于 300 个连接,
                    -- 所以我们故意在这里稍微延迟一下以符合
                    -- 200 个连接的限制。
                    -- ngx.log(ngx.WARN, "延迟")
                    ngx.sleep(delay)
                end
            }

            # 内容处理程序在这里。如果是 content_by_lua,那么您可以
            # 将上面的 Lua 代码合并到您的
            # content_by_lua 的 Lua 处理程序中,以节省一点 CPU 时间。

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- 如果您在内容阶段使用了上游模块,
                    -- 那么您可能想在下面使用 $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 代码合并到您的
            # content_by_lua 的 Lua 处理程序中,以节省一点 CPU 时间。

            log_by_lua_block {
                local ctx = ngx.ctx
                local lim = ctx.limit_conn
                if lim then
                    -- 如果您在内容阶段使用了上游模块,
                    -- 那么您可能想在下面使用 $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 用户控制和限制流量,无论是请求速率还是请求并发(或两者)。

请查看这些 Lua 模块的文档以获取更多详细信息。

该库提供了比 NGINX 的标准模块 ngx_limit_reqngx_limit_conn 更灵活的替代方案。例如,该库提供的基于 Lua 的限制器可以在任何上下文中使用,例如在下游 SSL 握手过程之前(如 ssl_certificate_by_lua)或在发出后端请求之前。

nginx.conf

http { ... }

然后在 Lua 中加载该库提供的模块之一。例如,

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

另见

GitHub

您可以在 nginx-module-limit-traffic 的 GitHub 仓库 中找到该模块的其他配置提示和文档。