利用Nginx、Lua脚本与Redis构建高效IP访问频率限制系统

使用 Nginx、Lua 脚本和 Redis 可以实现一个自动封禁访问频率过高 IP 的功能。这种架构非常适合处理高并发场景下的访问控制。以下是一个简单的实现步骤:

图片[1]_利用Nginx、Lua脚本与Redis构建高效IP访问频率限制系统_知途无界

1. 环境准备

  • Nginx:安装并配置 Nginx。
  • LuaJIT:安装 LuaJIT,因为 Nginx 的 Lua 模块需要 LuaJIT。
  • Nginx Lua 模块:安装 lua-nginx-module 和 ngx_devel_kit
  • Redis:安装并配置 Redis,用于存储访问频率数据。

2. 安装 Nginx 和 Lua 模块

你可以通过源码编译 Nginx 并添加 Lua 模块。以下是一个简单的编译步骤:

# 下载 Nginx 源码
wget http://nginx.org/download/nginx-1.21.3.tar.gz
tar -zxvf nginx-1.21.3.tar.gz
cd nginx-1.21.3/
# 下载 LuaJIT 和 Nginx Lua 模块
wget https://github.com/openresty/lua-nginx-module/archive/v0.10.20.tar.gz -O lua-nginx-module-0.10.20.tar.gz
wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.1.tar.gz -O ngx_devel_kit-0.3.1.tar.gz
tar -zxvf lua-nginx-module-0.10.20.tar.gz
tar -zxvf ngx_devel_kit-0.3.1.tar.gz
# 下载 LuaJIT
wget https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz -O LuaJIT-2.1.0-beta3.tar.gz
tar -zxvf LuaJIT-2.1.0-beta3.tar.gz
cd LuaJIT-2.1.0-beta3
make PREFIX=/usr/local/luajit
make install PREFIX=/usr/local/luajit
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1
# 编译 Nginx
cd ../nginx-1.21.3
./configure --prefix=/usr/local/nginx \
--add-module=../ngx_devel_kit-0.3.1 \
--add-module=../lua-nginx-module-0.10.20 \
--with-ld-opt="-Wl,-rpath,/usr/local/luajit/lib" \
--with-http_ssl_module
make
make install
# 下载 Nginx 源码  
wget http://nginx.org/download/nginx-1.21.3.tar.gz  
tar -zxvf nginx-1.21.3.tar.gz  
cd nginx-1.21.3/  
  
# 下载 LuaJIT 和 Nginx Lua 模块  
wget https://github.com/openresty/lua-nginx-module/archive/v0.10.20.tar.gz -O lua-nginx-module-0.10.20.tar.gz  
wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.1.tar.gz -O ngx_devel_kit-0.3.1.tar.gz  
tar -zxvf lua-nginx-module-0.10.20.tar.gz  
tar -zxvf ngx_devel_kit-0.3.1.tar.gz  
  
# 下载 LuaJIT  
wget https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz -O LuaJIT-2.1.0-beta3.tar.gz  
tar -zxvf LuaJIT-2.1.0-beta3.tar.gz  
cd LuaJIT-2.1.0-beta3  
make PREFIX=/usr/local/luajit  
make install PREFIX=/usr/local/luajit  
export LUAJIT_LIB=/usr/local/luajit/lib  
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1  
  
# 编译 Nginx  
cd ../nginx-1.21.3  
./configure --prefix=/usr/local/nginx \  
            --add-module=../ngx_devel_kit-0.3.1 \  
            --add-module=../lua-nginx-module-0.10.20 \  
            --with-ld-opt="-Wl,-rpath,/usr/local/luajit/lib" \  
            --with-http_ssl_module  
make  
make install
# 下载 Nginx 源码 wget http://nginx.org/download/nginx-1.21.3.tar.gz tar -zxvf nginx-1.21.3.tar.gz cd nginx-1.21.3/ # 下载 LuaJIT 和 Nginx Lua 模块 wget https://github.com/openresty/lua-nginx-module/archive/v0.10.20.tar.gz -O lua-nginx-module-0.10.20.tar.gz wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.1.tar.gz -O ngx_devel_kit-0.3.1.tar.gz tar -zxvf lua-nginx-module-0.10.20.tar.gz tar -zxvf ngx_devel_kit-0.3.1.tar.gz # 下载 LuaJIT wget https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz -O LuaJIT-2.1.0-beta3.tar.gz tar -zxvf LuaJIT-2.1.0-beta3.tar.gz cd LuaJIT-2.1.0-beta3 make PREFIX=/usr/local/luajit make install PREFIX=/usr/local/luajit export LUAJIT_LIB=/usr/local/luajit/lib export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1 # 编译 Nginx cd ../nginx-1.21.3 ./configure --prefix=/usr/local/nginx \ --add-module=../ngx_devel_kit-0.3.1 \ --add-module=../lua-nginx-module-0.10.20 \ --with-ld-opt="-Wl,-rpath,/usr/local/luajit/lib" \ --with-http_ssl_module make make install

3. 配置 Nginx 和 Lua 脚本

在 Nginx 配置文件中(通常是 /usr/local/nginx/conf/nginx.conf),添加 Lua 脚本和访问控制逻辑。

http {
lua_shared_dict my_limit_store 10m;
lua_package_path "/path/to/lua/scripts/?.lua;;";
server {
listen 80;
location / {
access_by_lua_file /path/to/lua/scripts/rate_limit.lua;
proxy_pass http://your_backend; # 替换为你的后端地址
}
}
}
http {  
    lua_shared_dict my_limit_store 10m;  
    lua_package_path "/path/to/lua/scripts/?.lua;;";  
  
    server {  
        listen 80;  
  
        location / {  
            access_by_lua_file /path/to/lua/scripts/rate_limit.lua;  
            proxy_pass http://your_backend;  # 替换为你的后端地址  
        }  
    }  
}
http { lua_shared_dict my_limit_store 10m; lua_package_path "/path/to/lua/scripts/?.lua;;"; server { listen 80; location / { access_by_lua_file /path/to/lua/scripts/rate_limit.lua; proxy_pass http://your_backend; # 替换为你的后端地址 } } }

4. 编写 Lua 脚本

在 /path/to/lua/scripts/rate_limit.lua 中编写 Lua 脚本,用于访问控制和 Redis 交互。

local limit_req = require "resty.limit.req"
-- 初始化限制对象,参数为 Redis 实例、存储的 key 前缀、存储的桶大小(滑动窗口大小)、速率(每秒多少请求)
local lim, err = limit_req.new("my_limit_store", "zone_key", 10, 20)
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 -- 使用客户端 IP 作为 key
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.log(ngx.ERR, "request got rejected due to rate limiting")
return ngx.exit(429) -- 返回 429 Too Many Requests
else
ngx.log(ngx.ERR, "failed to limit request: ", err)
return ngx.exit(500)
end
end
-- 如果请求被限速,delay 会返回需要延迟的时间(秒),可以将其转化为毫秒后 sleep
if delay >= 0.001 then
ngx.sleep(delay)
end
-- 可以在这里添加额外的逻辑,比如记录日志到 Redis 或其他系统
local limit_req = require "resty.limit.req"  
  
-- 初始化限制对象,参数为 Redis 实例、存储的 key 前缀、存储的桶大小(滑动窗口大小)、速率(每秒多少请求)  
local lim, err = limit_req.new("my_limit_store", "zone_key", 10, 20)  
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  -- 使用客户端 IP 作为 key  
local delay, err = lim:incoming(key, true)  
if not delay then  
    if err == "rejected" then  
        ngx.log(ngx.ERR, "request got rejected due to rate limiting")  
        return ngx.exit(429)  -- 返回 429 Too Many Requests  
    else  
        ngx.log(ngx.ERR, "failed to limit request: ", err)  
        return ngx.exit(500)  
    end  
end  
  
-- 如果请求被限速,delay 会返回需要延迟的时间(秒),可以将其转化为毫秒后 sleep  
if delay >= 0.001 then  
    ngx.sleep(delay)  
end  
  
-- 可以在这里添加额外的逻辑,比如记录日志到 Redis 或其他系统
local limit_req = require "resty.limit.req" -- 初始化限制对象,参数为 Redis 实例、存储的 key 前缀、存储的桶大小(滑动窗口大小)、速率(每秒多少请求) local lim, err = limit_req.new("my_limit_store", "zone_key", 10, 20) 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 -- 使用客户端 IP 作为 key local delay, err = lim:incoming(key, true) if not delay then if err == "rejected" then ngx.log(ngx.ERR, "request got rejected due to rate limiting") return ngx.exit(429) -- 返回 429 Too Many Requests else ngx.log(ngx.ERR, "failed to limit request: ", err) return ngx.exit(500) end end -- 如果请求被限速,delay 会返回需要延迟的时间(秒),可以将其转化为毫秒后 sleep if delay >= 0.001 then ngx.sleep(delay) end -- 可以在这里添加额外的逻辑,比如记录日志到 Redis 或其他系统

5. 启动和测试

  • 启动 Nginx:/usr/local/nginx/sbin/nginx
  • 启动 Redis:redis-server

现在,Nginx 将根据配置对访问频率进行限制,并自动封禁访问频率过高的 IP。你可以通过并发请求测试工具(如 ab 或 wrk)来验证这一功能。

6. 进一步优化

  • 动态配置:可以通过 Lua 脚本读取配置文件或环境变量来动态调整限速策略。
  • 日志记录:可以将被限速的 IP 记录到 Redis 或其他日志系统中,以便后续分析。
  • 白名单:添加白名单逻辑,允许特定 IP 不受限速策略的影响。

通过上述步骤,你可以使用 Nginx、Lua 脚本和 Redis 实现一个高效的访问频率限制系统。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞12 分享
The God only arranges a happy ending. If it is not happy, it means that it is not the final result.
上天只会安排的快乐的结局。如果不快乐,说明还不是最后结局
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容