自动摘要
正在生成中……
可以通过 OpenResty 的 Lua 模块(如 lua-resty-limit-req 模块)来控制用户的访问频率。例如,可以限制某个 IP 在 10 秒内不能超过 3 次请求。
lua-resty-limit-req
并没有单独的库,实际上限流功能已经包含在 lua-resty-limit-traffic
这个库中。这个库提供了多种限流功能,包括请求速率限制(类似 limit_req
),以及连接数限制等。
lua-resty-limit-traffic
包含的限流模块:
-
请求限流模块 (
resty.limit.req
):用于请求速率限制,类似 Nginx 的 limit_req
。
-
连接限流模块 (
resty.limit.conn
):用于并发连接数限制。
-
漏桶算法 (
resty.limit.count
):用于基于计数的限流。
因此,lua-resty-limit-req
功能实际上已经集成在 lua-resty-limit-traffic
中,安装和使用 lua-resty-limit-traffic
即可。
安装 lua-resty-limit-traffic
:
-
使用 opm
安装:
如果你已经有 opm
,可以通过以下命令安装:
opm get openresty/lua-resty-limit-traffic
-
通过 GitHub 安装:
你也可以从 GitHub 直接获取:
git clone https://github.com/openresty/lua-resty-limit-traffic.git
然后将其复制到 OpenResty 的 lua_package_path
中指定的目录。
使用 resty.limit.req
进行请求限流:
安装 lua-resty-limit-traffic
后,你可以继续使用之前的 resty.limit.req
限流方式。以下是如何在 Lua 模块中使用 resty.limit.req
进行限流的示例:
local limit_req = require "resty.limit.req"
-- 每秒最多0.3次请求(10秒最多3次)
local lim, err = limit_req.new("limit_req_store", 0.3, 3)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local key = ngx.var.http_cf_connecting_ip or ngx.var.remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "request rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
if delay > 0 then
ngx.sleep(delay)
end
通过这个配置,你可以对请求进行速率限制,功能与 Nginx 内建的 limit_req
类似。
对于使用 WordPress 且开启了 URL 重写(固定链接)的站点,你可以在 OpenResty/Nginx 中加入限流逻辑,同时保留 WordPress 的 URL 重写规则。以下是一个配置示例,包含 WordPress 典型的重写规则和访问频率限流的配置。
示例1:
对于使用 WordPress 且开启了 URL 重写(固定链接)的站点
,你可以在 OpenResty/Nginx 中加入限流逻辑,同时保留 WordPress 的 URL 重写规则。以下是一个配置示例,包含 WordPress 典型的重写规则和访问频率限流的配置。
http {
lua_shared_dict limit_req_store 10m;
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/wordpress; # 修改为你 WordPress 的根目录路径
index index.php index.html index.htm;
# 限制访问频率
location / {
access_by_lua_block {
local limit_req = require "resty.limit.req"
-- 每秒最多允许0.3次请求(10秒最多3次)
local lim, err = limit_req.new("limit_req_store", 0.3, 3)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", 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
ngx.log(ngx.WARN, "request is rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
-- 如果需要延迟响应,进行延迟
if delay > 0 then
ngx.sleep(delay)
end
}
# WordPress 的 URL 重写规则
try_files $uri $uri/ /index.php?$args;
}
# 处理 PHP 脚本
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 确保路径正确
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 静态文件处理
location ~* \.(jpg|jpeg|png|gif|css|js|ico|html)$ {
expires 30d;
access_log off;
}
# 限制访问 /wp-admin 区域
location /wp-admin/ {
access_by_lua_block {
local limit_req = require "resty.limit.req"
local lim, err = limit_req.new("limit_req_store", 0.5, 5) -- 每秒最多0.5次请求,突发5次
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "request is rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
if delay > 0 then
ngx.sleep(delay)
end
}
try_files $uri $uri/ /index.php?$args;
}
}
}
示例2:
分别对 PHP 请求和其他请求进行不同的限流设置
。你可以根据请求的类型(如 .php
文件)来分别应用不同的限流策略。
以下是针对 PHP 请求和其他请求设置不同限流规则的配置示例:
示例配置:
http {
lua_shared_dict limit_req_store 10m;
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/wordpress; # 修改为你 WordPress 的根目录路径
index index.php index.html index.htm;
# 对 PHP 文件请求进行限流(10秒内最多3次)
location ~ \.php$ {
access_by_lua_block {
local limit_req = require "resty.limit.req"
-- PHP 请求:每秒最多0.3次请求(10秒最多3次)
local lim, err = limit_req.new("limit_req_store", 0.3, 3)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "PHP request rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit PHP req: ", err)
return ngx.exit(500)
end
if delay > 0 then
ngx.sleep(delay)
end
}
# PHP-FPM 配置
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 确保路径正确
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 对非 PHP 文件的请求进行限流(10秒内最多5次,允许突发)
location / {
access_by_lua_block {
local limit_req = require "resty.limit.req"
-- 非 PHP 请求:每秒最多0.5次请求(10秒最多5次,允许突发)
local lim, err = limit_req.new("limit_req_store", 0.5, 10)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "Non-PHP request rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit non-PHP req: ", err)
return ngx.exit(500)
end
if delay > 0 then
ngx.sleep(delay)
end
}
try_files $uri $uri/ /index.php?$args;
}
# 静态文件处理
location ~* \.(jpg|jpeg|png|gif|css|js|ico|html)$ {
expires 30d;
access_log off;
}
}
}
配置说明:
-
location ~ \.php$
:此配置块专门处理 PHP 请求,并应用限流规则,限制为 10 秒内最多 3 次请求(local lim = limit_req.new("limit_req_store", 0.3, 3)
)。
-
location /
:此块处理除 PHP 文件之外的所有请求,限流为 10 秒内最多 5 次请求,允许突发(local lim = limit_req.new("limit_req_store", 0.5, 10)
)。
-
突发限制:在处理非 PHP 请求时,突发参数设置为
10
,允许突发请求数量更高,但在过高时仍会触发限流。
-
静态文件处理:对静态文件设置缓存,并禁用日志记录,减少服务器负载。
通过这个配置,PHP 请求和其他请求可以分别应用不同的限流策略,符合对访问频率的需求。
示例3:
当网站使用了 Cloudflare 的 CDN
时,默认情况下,你从 ngx.var.remote_addr
或 ngx.var.binary_remote_addr
获取到的是 Cloudflare 服务器的 IP,而不是用户的真实 IP。为了获取用户的真实 IP,需要使用 Cloudflare 添加的 X-Forwarded-For
或 CF-Connecting-IP
请求头。你可以根据这些请求头来获取用户的真实 IP。
修改步骤:
-
使用 CF-Connecting-IP
获取真实 IP
Cloudflare 会将用户的真实 IP 添加到 CF-Connecting-IP
请求头中。因此,你可以在限流逻辑中使用这个请求头来代替 ngx.var.remote_addr
。
-
修改 access_by_lua_block
来读取真实 IP
使用 ngx.var.http_cf_connecting_ip
变量获取 CF-Connecting-IP
,并替换原来的 ngx.var.binary_remote_addr
。
修改后的配置示例:
http {
# 设置 Lua 模块路径
lua_package_path "/opt/lua-resty-limit-traffic/lib/?.lua;;";
# 定义共享内存区域,用于存储限流数据
lua_shared_dict limit_req_store 10m;
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/wordpress; # 修改为你 WordPress 的根目录路径
index index.php index.html index.htm;
# 对 PHP 文件请求进行限流(10秒内最多3次)
location ~ \.php$ {
access_by_lua_block {
-- 引入 resty.limit.req 模块
local limit_req = require "resty.limit.req"
-- PHP 请求:每秒最多0.3次请求(10秒最多3次)
local lim, err = limit_req.new("limit_req_store", 0.3, 3)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
-- 使用 CF-Connecting-IP 作为限流键
local key = ngx.var.http_cf_connecting_ip or ngx.var.remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "PHP request rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit PHP req: ", err)
return ngx.exit(500)
end
-- 如果请求延迟存在,则进行休眠
if delay > 0 then
ngx.sleep(delay)
end
}
# PHP-FPM 配置
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 确保路径正确
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 对非 PHP 文件的请求进行限流(10秒内最多5次,允许突发)
location / {
access_by_lua_block {
-- 引入 resty.limit.req 模块
local limit_req = require "resty.limit.req"
-- 非 PHP 请求:每秒最多0.5次请求(10秒最多5次,允许突发)
local lim, err = limit_req.new("limit_req_store", 0.5, 10)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
-- 使用 CF-Connecting-IP 作为限流键
local key = ngx.var.http_cf_connecting_ip or ngx.var.remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.WARN, "Non-PHP request rejected due to rate limiting")
return ngx.exit(429)
end
ngx.log(ngx.ERR, "failed to limit non-PHP req: ", err)
return ngx.exit(500)
end
-- 如果请求延迟存在,则进行休眠
if delay > 0 then
ngx.sleep(delay)
end
}
# WordPress 固定链接处理
try_files $uri $uri/ /index.php?$args;
}
# 静态文件处理,禁止访问日志并设置长时间缓存
location ~* \.(jpg|jpeg|png|gif|css|js|ico|html)$ {
expires 30d;
access_log off;
}
}
}
配置说明:
-
ngx.var.http_cf_connecting_ip
:从 Cloudflare 发送的请求头中提取用户的真实 IP。如果 Cloudflare 没有提供这个请求头,默认会使用 ngx.var.remote_addr
。
-
限流逻辑:修改后的限流逻辑会基于
CF-Connecting-IP
(即用户的真实 IP)进行限流,而不是 Cloudflare 的服务器 IP。
通过这种方式,你可以正确地获取用户的真实 IP,并应用限流策略。