openresty开发系列37--nginx-lua-redis实现访问频率控制
openresty開發(fā)系列37--nginx-lua-redis實(shí)現(xiàn)訪問頻率控制
一)需求背景
在高并發(fā)場景下為了防止某個(gè)訪問ip訪問的頻率過高,有時(shí)候會(huì)需要控制用戶的訪問頻次
在openresty中,可以找到:
set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua等方法。
那么訪問控制應(yīng)該是,access階段。
我們用Nginx+Lua+Redis來做訪問限制主要是考慮到高并發(fā)環(huán)境下快速訪問控制的需求。
二)設(shè)計(jì)方案
我們用redis的key表示用戶,value表示用戶的請求頻次,再利用過期時(shí)間實(shí)現(xiàn)單位時(shí)間;
現(xiàn)在我們要求10秒內(nèi)只能訪問10次frequency請求,超過返回403
1)首先為nginx.conf配置文件,nginx.conf部分內(nèi)容如下:
location /frequency {
?? ?access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua;
?? ?echo "訪問成功";
}
?
2)編輯/usr/local/lua/access_by_limit_frequency.lua
?
local function close_redis(red) if not red then returnend --釋放連接(連接池實(shí)現(xiàn)) local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --連接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("set keepalive error : ", err) end endlocal function errlog(...)ngx.log(ngx.ERR, "redis: ", ...) endlocal redis = require "resty.redis" --引入redis模塊 local red = redis:new() --創(chuàng)建一個(gè)對象,注意是用冒號(hào)調(diào)用的--設(shè)置超時(shí)(毫秒) red:set_timeout(1000) --建立連接 local ip = "10.11.0.215" local port = 6379 local ok, err = red:connect(ip, port) if not ok then close_redis(red)errlog("Cannot connect");return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end local key = "limit:frequency:login:"..ngx.var.remote_addr--得到此客戶端IP的頻次 local resp, err = red:get(key) if not resp then close_redis(red)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 獲取值失敗 end if resp == ngx.null then red:set(key, 1) -- 單位時(shí)間 第一次訪問red:expire(key, 10) --10秒時(shí)間 過期 end if type(resp) == "string" then if tonumber(resp) > 10 then -- 超過10次 close_redis(red)return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403end end--調(diào)用API設(shè)置key ok, err = red:incr(key) if not ok then close_redis(red)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 報(bào)錯(cuò) end close_redis(red)# 當(dāng)redis設(shè)置了密碼時(shí),需要用red:auth() 方法進(jìn)行驗(yàn)證
# vim /usr/local/lua/access_by_limit_frequency.lualocal function close_redis(red)if not red thenreturnendlocal pool_max_idle_time = 10000local pool_size = 100local ok, err = red:set_keepalive(pool_max_idle_tme, pool_size)if not ok thenngx.say("set keepalive err : ", err)end endlocal function errlog(...)ngx.log(ngx.ERR, "redis: ", ...) endlocal redis = require "resty.redis" local red = redis:new()red:set_timeout(1000) local ip = "10.11.0.215" local port = 6379 local ok,err = red:connect(ip, port) if not ok thenclose_redis(red)errlog("connot connect");return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) endlocal count, err = red:get_reused_times() if 0 == count then ----新建連接,需要認(rèn)證密碼ok, err = red:auth("redis123")if not ok thenngx.say("failed to auth: ", err)returnend elseif err then ----從連接池中獲取連接,無需再次認(rèn)證密碼ngx.say("failed to get reused times: ", err)return endlocal key = "limit:frequency:login: " ..ngx.var.remote_addr--得到此客戶端IP的頻次 local resp,err = red:get(key) if not resp thenclose_redis(red)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) endif resp == ngx.null thenred:set(key, 1)red:expire(key, 100) -- 設(shè)置過期時(shí)間,即返回403的時(shí)間為100秒 end--ngx.say("connect ok") --ngx.say("resp:",resp) if type(resp) == "string" thenif tonumber(resp) > 10 thenclose_redis(red)return ngx.exit(ngx.HTTP_FORBIDDEN)end endok, err = red:incr(key) if not ok thenclose_redis(red)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) endclose_redis(red)請求地址:/frequency
10秒內(nèi) 超出10次 ,返回403
10秒后,又可以訪問了
在Nginx需要限速的location中引用上述腳本配置示例:
location /user/ {
?? ?set $business "USER";
?? ?access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
?? ?proxy_redirect off;
?? ?proxy_set_header??????? Host $host;
?? ?proxy_set_header??????? X-Real-IP $remote_addr;
?? ?proxy_set_header??????? X-Forwarded-For $proxy_add_x_forwarded_for;
?? ?proxy_pass http://user_224/user/;
}
注:對于有大量靜態(tài)資源文件(如:js、css、圖片等)的前端頁面可以設(shè)置只有指定格式的請求才進(jìn)行訪問限速,示例代碼如下:
location /h5 {
if ($request_uri ~ .*\.(html|htm|jsp|json)) {
?? ?set $business "H5";
?? ?access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
}
?? ?proxy_redirect off;
?? ?proxy_set_header??????? Host $host;
?? ?proxy_set_header??????? X-Real-IP $remote_addr;
?? ?proxy_set_header??????? X-Forwarded-For $proxy_add_x_forwarded_for;
?? ?proxy_pass http://h5_224/h5;
}
如果我們想整個(gè)網(wǎng)站 都加上這個(gè)限制條件,那只要把
access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua;
這個(gè)配置,放在server部分,讓所有的location 適用就行了
轉(zhuǎn)載于:https://www.cnblogs.com/reblue520/p/11457640.html
總結(jié)
以上是生活随笔為你收集整理的openresty开发系列37--nginx-lua-redis实现访问频率控制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hadoop实例之Java代码实现利用M
- 下一篇: openresty开发系列39--ngi