openresty开发系列37--nginx-lua-redis实现访问频率控制
openresty開發系列37--nginx-lua-redis實現訪問頻率控制
一)需求背景
在高并發場景下為了防止某個訪問ip訪問的頻率過高,有時候會需要控制用戶的訪問頻次
在openresty中,可以找到:
set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua等方法。
那么訪問控制應該是,access階段。
我們用Nginx+Lua+Redis來做訪問限制主要是考慮到高并發環境下快速訪問控制的需求。
二)設計方案
我們用redis的key表示用戶,value表示用戶的請求頻次,再利用過期時間實現單位時間;
現在我們要求10秒內只能訪問10次frequency請求,超過返回403
1)首先為nginx.conf配置文件,nginx.conf部分內容如下:
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 --釋放連接(連接池實現) 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() --創建一個對象,注意是用冒號調用的--設置超時(毫秒) 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) -- 單位時間 第一次訪問red:expire(key, 10) --10秒時間 過期 end if type(resp) == "string" then if tonumber(resp) > 10 then -- 超過10次 close_redis(red)return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403end end--調用API設置key ok, err = red:incr(key) if not ok then close_redis(red)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 報錯 end close_redis(red)# 當redis設置了密碼時,需要用red:auth() 方法進行驗證
# 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 ----新建連接,需要認證密碼ok, err = red:auth("redis123")if not ok thenngx.say("failed to auth: ", err)returnend elseif err then ----從連接池中獲取連接,無需再次認證密碼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) -- 設置過期時間,即返回403的時間為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秒內 超出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/;
}
注:對于有大量靜態資源文件(如:js、css、圖片等)的前端頁面可以設置只有指定格式的請求才進行訪問限速,示例代碼如下:
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;
}
如果我們想整個網站 都加上這個限制條件,那只要把
access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua;
這個配置,放在server部分,讓所有的location 適用就行了
轉載于:https://www.cnblogs.com/reblue520/p/11457640.html
總結
以上是生活随笔為你收集整理的openresty开发系列37--nginx-lua-redis实现访问频率控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hadoop实例之Java代码实现利用M
- 下一篇: openresty开发系列38--通过L