OpenResty 实现动态黑名单需“内存缓存+Redis统一存储+定时同步”,必须用 OpenResty(非原生 Nginx+Lua),通过 lua_shared_dict 定义共享字典、init_worker_by_lua_file 定时全量加载 Redis SET 数据,access_by_lua_file 快速查内存拦截,封解操作须用 SADD/SREM/DEL 操作 Redis SET。

OpenResty 实现动态黑名单封禁与解封,核心是“内存缓存 + Redis 统一存储 + 定时同步”,不是每次请求都查 Redis,也不靠 reload 配置。关键在于让封禁毫秒生效、多 worker 一致、解封自动及时。
必须用 OpenResty,不能只装 Nginx + Lua 模块
原生 Nginx 编译 lua-module 后,缺失 lua_shared_dict、init_worker_by_lua_file、ngx.shared.DICT 等能力。配置里一出现 unknown directive "lua_shared_dict" 或 attempt to index global 'resty',就说明环境不达标。
正确做法:
- CentOS 直接
yum install openresty,Ubuntu 用apt install openresty - 版本不低于 1.25.3.1,验证命令:
openresty -v输出含 “OpenResty” 字样,且openresty -t通过
共享字典定义与定时全量加载
在 http 块顶层声明共享内存,作为所有 worker 共用的黑名单缓存:
lua_shared_dict ip_blacklist 50m;
大小按实际预估:IPv4 百万级 IP 占约 20MB,留余量更稳妥;若含 IPv6 或未来扩容,可设 100m。
用 init_worker_by_lua_file 启动每个 worker 的独立定时器(不是 init_by_lua_block):
init_worker_by_lua_file /path/to/load_blacklist.lua;
脚本逻辑(每 30–60 秒执行一次):
- 连接 Redis,执行
SMEMBERS ip_blacklist(Redis 中必须用 SET 类型,不能用 STRING/HASH) - 调用
ngx.shared.ip_blacklist:flush_all() - 遍历结果,逐条
set(ip, true)
这样保证最终一致性,避免增量更新带来的状态不一致风险。
访问阶段快速拦截(不碰 Redis)
在 server 或 location 块中挂载:
access_by_lua_file /path/to/check_blacklist.lua;
脚本内容极简,全程无网络 IO:
- 按顺序取真实 IP:
ngx.var.http_x_real_ip→string.match(ngx.var.http_x_forwarded_for, "([^,]+)")→ngx.var.remote_addr - 查共享字典:
local blacklist = ngx.shared.ip_blacklist,再blacklist:get(ip) - 命中即
ngx.exit(403),不走后续 proxy_pass 或静态服务
耗时稳定在微秒级,完全绕过网络延迟。
封禁与解封的操作规范
运维或系统写入 Redis 时,必须统一操作 SET:
- 封 IP:
SADD ip_blacklist 192.168.1.100 - 批量封:
SADD ip_blacklist 1.1.1.1 2.2.2.2 3.3.3.3 - 解封单个:
SREM ip_blacklist 192.168.1.100 - 清空全部:
DEL ip_blacklist
禁止用 SET blacklist:1.1.1.1 1 EX 300 这类单 key 方式——无法批量管理、TTL 不统一、定时同步脚本读不到。
Redis 端建议开启密码认证、限制来源 IP,并确保 save 或 aof 配置合理,防止数据丢失。


















