有次公司的一些接口被人爬數(shù)據(jù),導致web端異常緩慢。以前就知道openresty有這個功能,認識人往往就是這樣,問題發(fā)生了,才去被動解決,反思!
調(diào)研解決方案如下
1.把nginx換成openresty
2.openresy + redis 去統(tǒng)計訪問者訪問次數(shù),設置閾值,高于閾值直接返回403,一段時間后放開
調(diào)研結果如下
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內(nèi)無法再次訪問
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>