上篇分布式--OpenResty+lua+Redis中,我们了解了nginx结合lua脚本的强大之处,lua结合反向代理,可以对http请求提前做一些处理,来保证业务服务器的安全性和单一职责原则,以及结合Redis提升读写缓存的效率与持久化能力
一、实现限流
DOS攻击是常见的攻击服务器的方式,限流可以做到防止暴力访问服务器,可以从流量方面进行限制,也可以从请求次数方面进行限制,下面为使用lua对http请求次数进行限制
1. 环境准备
我们需要两个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/;
- }
- }
测试下浏览器访问:
2. 编写lua脚本
2.1 日志脚本
limit_log.lua
内容:
- -- 获取ip
- local ip = ngx.var.remote_addr
-
- ngx.log(ngx.INFO,"ip limit log,ip:"..ip)
2.2 连接脚本
需要实现的功能为:一个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
3. 测试
重新加载下nginx配置:
./nginx -p /usr/local/openresty/nginx -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload
效果:
服务器日志:
二、实现防爬虫
爬虫有好的,也有坏的,恶意的爬虫会不断爬取网站信息,导致服务器性能下降,解决爬虫的方式有限制user_agent、限制ip、添加验证码、限制cookie。下面是限制ip的方式
1. 环境准备
首先我们的黑名单集合是从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;
- }
- }
- }
2. 编写lua脚本
我们需要实现,当一个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
3. 测试
重新加载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实现限流和防爬虫