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 用户控制和限制流量,无论是请求速率还是请求并发(或两者)。
- 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"
另见
- 模块 resty.limit.req
- 模块 resty.limit.count
- 模块 resty.limit.conn
- 模块 resty.limit.traffic
- ngx_lua 模块: https://github.com/openresty/lua-nginx-module
- OpenResty: https://openresty.org/
GitHub
您可以在 nginx-module-limit-traffic 的 GitHub 仓库 中找到该模块的其他配置提示和文档。