社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  NGINX

开源生态新解法:Nginx+Lua 实现毫秒级非法 IP 熔断机制

Linux就该这么学 • 2 周前 • 57 次点击  

链接:https://juejin.cn/post/7475693874510315583

图片

背景:访问时不时会被暴力刷量,爬虫和恶意攻击导致数据库,服务等瘫痪。。需求:在Nginx上实现一个动态拦截IP的方法,具体是当某个IP在1分钟内访问超过60次时,将其加入Redis并拦截,拦截时间默认1天。

技术选型:使用Nginx+Lua+Redis的方法。这种方案通过Lua脚本在Nginx处理请求时检查Redis中的黑名单,同时统计访问频率,超过阈值就封禁。这应该符合用户的需求。

需要结合Lua脚本和Redis的计数功能。安装OpenResty,配置Nginx的Lua模块,编写Lua脚本统计访问次数,使用Redis存储和过期键,以及设置拦截逻辑。连接池的使用,避免频繁连接Redis影响性能。

一、环境准备

  1. 安装OpenResty
    OpenResty集成了Nginx和Lua模块,支持直接运行Lua脚本:

    # Ubuntu/Debian
    sudo apt-get install openresty
    # CentOS
    yum install openresty

  2. 安装Redis服务

    sudo apt-get install redis-server  # Debian系
    sudo yum install redis             # RedHat系


二、Nginx配置

  1. 主配置文件(nginx.conf)
    http块中添加共享内存和Lua脚本路径:

    http {
        lua_package_path "/usr/local/openresty/lualib/?.lua;;";
        lua_shared_dict ip_limit 10m;  # 共享内存区

        server {
            listen 80;
            server_name _;

            location / {
                access_by_lua_file /usr/local/lua/ip_block.lua;  # 核心拦截脚本
                root /var/www/html;
            }
        }
    }


三、Lua脚本实现动态拦截

  1. 脚本路径
    创建Lua脚本:/usr/local/lua/ip_block.lua

  2. 脚本内容

    local redis = require "resty.redis"
    local red = redis:new()

    -- Redis连接参数
    local redis_host ="127.0.0.1"
    local redis_port =6379
    local redis_timeout =1000-- 毫秒
    local redis_auth =nil-- 无密码留空

    -- 拦截参数
    local block_time =86400-- 封禁时间(1天)
    local time_window =60-- 统计窗口(1分钟)
    local max_requests =60-- 最大请求数

    -- 获取客户端IP
    localfunctionget_client_ip()
    local headers = ngx.req.get_headers()
    return headers["X-Real-IP"]or headers["x_forwarded_for"]or ngx.var.remote_addr
    end

    -- 连接Redis
    localfunctionconnect_redis()
        red:set_timeout(redis_timeout)
    local ok, err = red:connect(redis_host, redis_port)
    ifnot ok then
            ngx.log(ngx.ERR,"Redis连接失败: ", err)
    returnnil
    end
    if redis_auth then
    local ok, err = red:auth(redis_auth)
    ifnot ok then ngx.log(ngx.ERR,"Redis认证失败: ", err)end
    end
    return ok
    end

    -- 主逻辑
    local client_ip =get_client_ip()
    local counter_key ="limit:count:".. client_ip
    local block_key ="limit:block:".. client_ip

    -- 检查是否已封禁
    local is_blocked, err = red:get(block_key)
    iftonumber(is_blocked)==1then
        ngx.exit(ngx.HTTP_FORBIDDEN)-- 直接返回403
    end

    -- 统计请求次数
    connect_redis()
    local current_count = red:incr(counter_key)
    if current_count ==1then
        red:expire(counter_key, time_window)-- 首次设置过期时间
    end

    -- 触发封禁条件
    if current_count > max_requests then
        red:setex(block_key, block_time,1)-- 封禁并设置1天过期
        red:del(counter_key)-- 删除计数器
        ngx.exit (ngx.HTTP_FORBIDDEN)
    end

    -- 释放Redis连接
    red:set_keepalive(10000,100)


四、性能优化

  1. Redis连接池
    通过set_keepalive复用连接,避免频繁建立TCP连接

  • 共享内存缓存
    使用lua_shared_dict缓存高频访问IP,减少Redis查询压力

  1. 异步日志记录
    封禁操作异步写入日志文件,避免阻塞请求处理:

    ngx.timer.at(0,function()
    local log_msg = string.format("%s - IP %s blocked at %s",
            ngx.var.host, client_ip, ngx.localtime())
    local log_file = io.open( "/var/log/nginx/blocked_ips.log","a")
        log_file:write(log_msg,"\n")
        log_file:close()
    end)


五、验证与测试

  1. 手动触发封禁

    # 模拟高频请求
    ab -100-c10 http://your-domain.com/
    # 检查Redis
    redis-cli keys "limit:block:*"

  2. 自动解封验证
    等待24小时后检查封禁IP是否自动删除:

    redis-cli ttl "limit:block:1.2.3.4"# 返回剩余秒数


六、扩展方案

  1. 分布式封禁
    在多台Nginx服务器间共享Redis黑名单,实现集群级拦截

    可视化监控
    通过Grafana+Prometheus展示实时拦截数据:

    # 采集Redis指标
    prometheus-redis-exporter --redis.address=localhost:6379

    动态调整阈值
    通过Redis Hash存储不同路径的拦截规则:

    local rule_key="limit:rule:" .. ngx.var.uri
    local custom_rule=red:hget(rule_key, "max_requests")


引用说明

  • 核心拦截逻辑参考了Nginx+Lua+Redis的经典架构设计

  • Redis键过期机制确保自动解封

  • 性能优化方案借鉴了OpenResty最佳实践

END

想要学习Linux系统的读者可以点击"阅读原文" 按钮来了解书籍《Linux就该这么学》,同时也非常适合专业的运维人员阅读,成为辅助您工作的高价值工具书!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/182092
 
57 次点击