实时黑名单系统,如果用php脚本实现很容易,但是效率惨不忍睹呀。
要想速度快还的在nginx层实现阻塞。如果iptables 层阻塞速度更快,但是黑名单列表如果有更新就必须要重载配置,实现还是有难度的。php管理后台把黑名单ip写入到redis,nginx层使用lua脚本去redis查询ip是否在黑名单里,实现实时控制。
OpenResty就是嵌入了LuaJIT VM的Nginx,LuaJIT即采用C语言写的Lua代码的解释器。熟悉简单的Lua语言和nginx基础知识就可以上手开发简单功能了。
你可以单独安装OpenResty,如果是宝塔面板也可以直接切换版本 nginx openresty
直接上代码。
整个系统的瓶颈在redis,我们要优化redis连接,需要连接池。
lua_shared_dict my_cache 10m;
server
{
.........
先创建一个内存缓存区,名称:my_cache 容量:10m
ngx+lua 中没有全局变量我们需要把redis的链接函数存储到这个 my_cache 里面使用其他地方使用的时候直接读取缓存里的函数代码,再运行函数得到一个redis连接
local my_cache = ngx.shared.my_cache
local redis_connect_code = my_cache:get("redis_connect")
if not redis_connect_code then
local function redis_connect()
local redis = require "resty.redis"
local redis_host = "127.0.0.1"
local pool_size = 1000
red = redis:new()
red:set_timeout(1000)
red:set_pool_size(pool_size)
red:set_keepalive(10000,pool_size)
red:connect(redis_host,6379)
red:auth('635241')
red:select(1)
return red
end
redis_connect_code = string.dump(redis_connect)
my_cache:set('redis_connect',redis_connect_code)
end
local ip = ngx.var.remote_addr
if ip == '192.168.1.102' then
ngx.exit(ngx.OK)
end
local redis_connect=loadstring(redis_connect_code)
local red=redis_connect()
red:hincrby('store_ip:01',ip,1)
local blacklist, err=red:sismember("store_ip:black",ip)
if blacklist==1 then
red:hincrby('store_ip:00',ip,1)
ngx.exit(444)
end
以上是redis连接池代码好黑名单判断代码
把以上代码存储为 access.lua文件放到 /www/server/nginx/conf/ 目录
nginx站点vhosts配置文件
lua_shared_dict my_cache 10m;
server
{
listen 80;
listen 443 ssl;
http2 on;
...........
default_type 'application/json';
access_by_lua_file /www/server/nginx/conf/access.lua;
在这里解释一点
default_type 'application/json'; 强制返回json格式
access_by_lua_file /www/server/nginx/conf/access.lua; 这个代码就是引入上面的黑名单代码。
为什么不把这个代码放到 location / 里面,这样只阻塞api接口。
location / {
access_by_lua_file /www/server/nginx/conf/access.lua;
}
这样写似乎更规范?
但是我的项目用的是Laravel框架,这个框架需要伪静态配置,
location / {
try_files $uri $uri/ /index.php?$query_string;
}
这样就 两个location / {} 配置就冲突了。
可不可以把两个 location / 整合到一个里面,是可以的。
但是这个伪静态需要放在所有 location的最后。而黑名单阻塞需要放到所有location的最前面,那就只能不写location 防到外层 server下,这样缺点是,任何请求都会黑名单阻塞,包括请求静态资源,比如一个图片或css文件都会触发黑名单阻塞。