上篇分布式 --OpenResty+lua+Redis 中,我们了解了 nginx 结合 lua 脚本的强大之处,lua 结合反向代理,可以对 http 请求提前做一些处理,来保证业务服务器的安全性和单一职责原则,以及结合 Redis 提升读写缓存的效率与持久化能力
DOS 攻击是常见的攻击服务器的方式,限流可以做到防止暴力访问服务器,可以从流量方面进行限制,也可以从请求次数方面进行限制,下面为使用 lua 对 http 请求次数进行限制
我们需要两个 lua 脚本,一个用于记录日志,一个用于限流实现:
touch limit_log.lua touch limit_access.lua
修改 nginx 配置文件,对8050
端口的 http 访问进行限流:
error_log logs/error.log info; server { listen 8080; root html; location / { index index.html; } } server { listen 8050; location / { default_type text/html; # 表示连接都通过该脚本 access_by_lua_file /usr/local/openresty/nginx/lua/limit_access.lua; log_by_lua_file /usr/local/openresty/nginx/lua/limit_log.lua; proxy_pass http://localhost:8080/; } }
测试下浏览器访问:
limit_log.lua
内容:
-- 获取ip local ip = ngx.var.remote_addr ngx.log(ngx.INFO,"ip limit log,ip:"..ip)
需要实现的功能为:一个 ip,1s 内不得访问超过 2 次,否则限制访问 10s
下面是我们限流的流程:
limit_access.lua
内容:
-- 获取ip local ip = ngx.var.remote_addr -- 获取redis local redis = require "resty.redis" local red = redis:new() local ok,err = red:connect("127.0.0.1",6379) if not ok then return ngx.say("连接redis失败") end -- 判断该ip是否处于限流 local isLimitKey = ip.."limit" local isLimit = red:get(isLimitKey) ngx.log(ngx.INFO,"limitKey:"..isLimitKey.." val:",isLimit) if isLimit == '1' then return ngx.exit(503) end -- 判断访问次数是否大于阈值,阈值为1秒内访问超过2次 local countKey = ip.."limitCount" -- 访问次数加1后返回 local limitCount = red:incr(countKey) ngx.log(ngx.INFO,"countKey:"..countKey.." val:"..limitCount) if limitCount <= 2 then -- 没有超过,更新过期时间 red:expire(countKey,1) else -- 超过了,就设置限流,10s不可访问 red:setex(isLimitKey,10,1) red:set(countKey,0) end
重新加载下 nginx 配置:
./nginx -p /usr/local/openresty/nginx -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload
效果:
服务器日志:
爬虫有好的,也有坏的,恶意的爬虫会不断爬取网站信息,导致服务器性能下降,解决爬虫的方式有限制 user_agent、限制 ip、添加验证码、限制 cookie。下面是限制 ip 的方式
首先我们的黑名单集合是从 Redis 中获取的,在 Redis 中新增一个set
:
sadd black_set 192.168.42.170 192.168.42.171
我们也不需要一直都从 redis 获取,nginx 有自带的共享内存
,定时从 Redis 中获取,然后存入共享内存
中,在有效时限内使用共享内存
中的数据即可,下面是 nginx 的配置:
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # 定义共享内存 最大2mb lua_shared_dict black_set 2m; server { listen 8080; root html; location / { index index.html; } } # 防爬虫 server { listen 8060; location / { default_type text/html; access_by_lua_file /usr/local/openresty/nginx/lua/http-black.lua; proxy_pass http://localhost:8080; } } }
我们需要实现,当一个 ip 进行 http 访问时,判断如果该 ip 处于黑名单中,那么拦截该请求
创建脚本:
cd /usr/local/openresty/nginx/lua/ vi http-black.lua
http-black.lua
内容:
-- 1. 获取共享内存黑名单 -- 获取黑名单更新时间 ngx.log(ngx.INFO,ip) local black_set = ngx.shared.black_set local update_time = black_set:get("update_time") -- 如果为空,或者距离当前时间大于2s,从redis中获取 if update_time == nil or ngx.now() - update_time > 2 then local redis = require "resty.redis" local red = redis:new(); local ok,err = red:connect("127.0.0.1",6379) if not ok then ngx.log(ngx.INFO,"redis connect failed") else local black_set_by_redis,err = red:smembers("black_set") -- 更新共享内存 black_set:flush_all() for k,v in pairs(black_set_by_redis) do black_set:set(v,true) ngx.log(ngx.INFO,v) end black_set:set("update_time",ngx.now()) end end -- 2. 请求ip是否在黑名单中 local ip = ngx.var.remote_addr ngx.log(ngx.INFO,ip) ngx.log(ngx.INFO,black_set:get(ip)) if black_set:get(ip) then return ngx.exit(503) end
重新加载 nginx 配置:
./nginx -p /usr/local/openresty/nginx/ -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload
访问结果:
redis 删除黑名单的 IP:
srem black_set 192.168.42.170
继续访问:
以上就是 nginx 使用 lua 脚本结合 redis 实现限流和防爬虫
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点 / 博客。
原始发表:2022-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除