软件开发 --- OpenResty 之初体验
OpenResty 业务场景示例:实现高效的 API Rate Limiting(API 请求速率限制)
在很多 Web 服务中,特别是对于公共 API,通常需要对访问进行限制,以防止恶意请求或者过度使用服务资源。OpenResty 提供了非常强大的 Lua 脚本支持,可以非常高效地实现请求速率限制(Rate Limiting),不仅能在服务端控制请求频率,还能避免数据库或后端系统的过度负载。
业务场景:实现一个 API 请求速率限制系统
假设你有一个公共 API 服务,要求每个用户在一分钟内最多只能请求 60 次。超过此限制的请求应该返回 429 HTTP 状态码,表示 "请求过多"(Too Many Requests)。
步骤 1:配置 Nginx 和 OpenResty
我们将使用 OpenResty 来实现这一业务逻辑,通过 Lua 脚本实现速率限制。
1.1 修改 Nginx 配置文件
打开 OpenResty 的 Nginx 配置文件(通常位于 /usr/local/openresty/nginx/conf/nginx.conf
):
sudo nano /usr/local/openresty/nginx/conf/nginx.conf
1.2 添加以下配置:
http {
# 创建一个共享内存空间,用于存储每个用户的请求次数
lua_shared_dict rate_limit_cache 10m; # 设置 10MB 的共享内存,用于存储请求计数
server {
listen 80;
location /api/ {
# 每个请求进入时,都会执行 Lua 脚本来检查请求速率
access_by_lua_block {
local rate_limit_cache = ngx.shared.rate_limit_cache
local client_ip = ngx.var.remote_addr -- 获取客户端的 IP 地址
local current_time = ngx.now() -- 获取当前时间
local key = "rate_limit:" .. client_ip -- 用客户端 IP 作为键
-- 获取该 IP 地址上次请求的时间和请求次数
local last_request_time = rate_limit_cache:get(key .. ":time")
local request_count = rate_limit_cache:get(key .. ":count")
if last_request_time then
-- 如果距离上次请求的时间小于 1 分钟(60秒)
if current_time - last_request_time < 60 then
if request_count >= 60 then
-- 如果在 1 分钟内请求次数超过限制,返回 429 错误
ngx.status = 429
ngx.say("Too Many Requests: Please try again later.")
ngx.exit(ngx.HTTP_OK)
else
-- 请求次数未超过限制,更新计数器
rate_limit_cache:incr(key .. ":count", 1)
end
else
-- 如果请求时间间隔大于 1 分钟,重置计数器
rate_limit_cache:set(key .. ":time", current_time)
rate_limit_cache:set(key .. ":count", 1)
end
else
-- 第一次请求该 IP,初始化时间和计数器
rate_limit_cache:set(key .. ":time", current_time)
rate_limit_cache:set(key .. ":count", 1)
end
}
# 正常处理请求
content_by_lua_block {
ngx.say("Request successfully processed")
}
}
}
}
步骤 2:解释代码
2.1 lua_shared_dict rate_limit_cache 10m;
- 这里创建了一个共享内存区域
rate_limit_cache
,大小为 10MB,用于存储每个用户的请求计数和请求时间。共享内存允许 OpenResty 在多个请求之间共享数据。
2.2 access_by_lua_block
access_by_lua_block
是 OpenResty 中的一个指令,允许在请求处理的初期阶段通过 Lua 脚本执行逻辑。我们使用 Lua 来实现速率限制。- 该 Lua 脚本根据客户端的 IP 地址 (
ngx.var.remote_addr
) 来存储和获取该 IP 的请求记录。 - 对于每个请求,我们检查客户端的 IP 是否在过去的 60 秒内已经超过了最大请求次数(60 次)。如果是,返回 HTTP 429 错误;如果没有,继续处理请求并更新请求计数。
2.3 ngx.shared.rate_limit_cache
ngx.shared.rate_limit_cache
是我们在 Nginx 配置中定义的共享内存区域。我们通过 Lua 脚本访问它,存储和读取请求计数和时间。
2.4 rate_limit_cache:incr
和 rate_limit_cache:set
rate_limit_cache:incr
用于递增指定键的值,代表请求计数。rate_limit_cache:set
用于设置指定键的值,初始化请求计数和时间。
2.5 速率限制逻辑
if current_time - last_request_time < 60
:判断当前请求和上一次请求之间的时间差是否小于 60 秒。rate_limit_cache:incr(key .. ":count", 1)
:如果请求次数在限制内,增加计数。- 如果请求次数超过 60 次,则返回 HTTP 429 错误,表示请求过多。
步骤 3:启动 OpenResty
- 启动 OpenResty 服务:
sudo /usr/local/openresty/nginx/sbin/nginx
- 检查是否启动成功:
curl http://localhost/api/
你会看到返回:
Request successfully processed
- 超过请求次数时的响应:
- 在一分钟内多次访问 API,例如:
curl http://localhost/api/
curl http://localhost/api/
...
- 如果请求次数超过了限制,服务会返回:
Too Many Requests: Please try again later.
步骤 4:停止 OpenResty
如果需要停止 OpenResty,可以使用以下命令:
sudo /usr/local/openresty/nginx/sbin/nginx -s stop
总结
在这个 API 请求速率限制 的业务场景中,我们使用 OpenResty 和 Lua 脚本来实现:
- 客户端 IP 限制:每个 IP 每分钟只能请求 60 次。
- 高效的缓存机制:通过共享内存
lua_shared_dict
来存储请求数据,避免了数据库和外部存储的压力。 - 动态响应控制:在请求超过限制时,通过 Lua 脚本灵活控制返回 429 错误。
这个示例展示了 OpenResty 在高并发 Web 应用中如何结合 Lua 实现复杂的业务逻辑,同时保持高效性和灵活性。