有次公司的一些接口被人爬数据,导致web端异常缓慢。以前就知道openresty有这个功能,认识人往往就是这样,问题发生了,才去被动解决,反思!
调研解决方案如下
1.把nginx换成openresty
2.openresy + redis 去统计访问者访问次数,设置阈值,高于阈值直接返回403,一段时间后放开
调研结果如下
nginx location段配置
location /redis_forbid {
default_type 'text/html';
access_by_lua_file lua/intercept.lua;
content_by_lua_block {
ngx.say("access")
}
}
intercept.lua
local redis = require("resty.redis")
local conn = redis:new()
conn:set_timeout(10000)
local redis_ip = "192.168.138.129"
local redis_port = 6379
local ok , err = conn:connect(redis_ip,redis_port)
if not ok then
ngx.say("connect to redis fatal error: " ,err)
return redis_close(conn)
end
-- ngx.exit(ngx.ERR) 客户端无法返回
local ttl = 60 -- key过期时间
local top_times = 10
local forbit_ttl = 100 --60内无法再次访问
local ip = ngx.var.remote_addr
local ip_total_times = conn:get(ip)
print("debug...................................." , conn:ttl(ip))
if ip_total_times ~= ngx.null then
if (ip_total_times == "-1") then
return ngx.exit(403)
else
this_ttl = conn:ttl(ip)
if (this_ttl == -1) then --没有设置超时时间
conn:set(ip,0)
conn:expire(ip,ttl)
return ngx.exit(ngx.OK)
end
v_times = tonumber(conn:get(ip)) + 1
if (v_times > top_times) then
conn:set(ip,-1)
conn:expire(ip,forbit_ttl)
return ngx.exit(ngx.OK)
else
print("debug 03")
conn:set(ip,v_times)
conn:expire(ip,this_ttl) -- 不要重新计时
return ngx.exit(ngx.OK)
end
end
else
print("key is not exists")
conn:set(ip,1)
conn:expire(ip,ttl)
return ngx.exit(ngx.OK) --step done,如access_by_lua_file模块此处后,进入到content_by_lua_file模块处理
end
再写段测试程序
package main
import (
"net/http"
"fmt"
"io/ioutil"
"os"
)
func main() {
for i := 1 ; i<= 20 ;i++ {
resp,err := http.Get("http://192.168.138.128/redis_forbid")
defer resp.Body.Close()
if err != nil {
os.Exit(-1)
}
//time.Sleep(1*time.Second)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("i is", i)
fmt.Println(string(body))
}
}
结果访问如下:
i is 12
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty/1.13.6.2</center>
</body>
</html>
网友评论