OpenResty中實(shí)現(xiàn)按QPS、時(shí)間范圍、來(lái)源IP進(jìn)行限流的方法
OpenResty是一個(gè)基于Nginx與Lua的高性能Web平臺(tái),它通過(guò)LuaJIT在Nginx中運(yùn)行高效的Lua腳本和模塊,可以用來(lái)處理復(fù)雜的網(wǎng)絡(luò)請(qǐng)求,并且支持各種流量控制和限制的功能。
近期研究在OpenResty中如何實(shí)現(xiàn),按QPS、時(shí)間范圍、來(lái)源IP進(jìn)行限流,以及動(dòng)態(tài)更新限流策略。今天將實(shí)現(xiàn)方案分享給大家。
一、在OpenResty中如何實(shí)現(xiàn),按QPS、時(shí)間范圍、來(lái)源IP進(jìn)行限流
使用OpenResty進(jìn)行限流的幾種常見(jiàn)方法:
按QPS(每秒查詢率)限流:
使用ngx_http_limit_req_module
模塊,可以限制每個(gè)客戶端的請(qǐng)求速率。這個(gè)模塊使用漏桶算法來(lái)控制請(qǐng)求的速率。
在Nginx配置文件中,你可以這樣設(shè)置:
http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; server { location / { limit_req zone=mylimit burst=5 nodelay; } } }
上面的配置定義了一個(gè)名為mylimit
的區(qū)域,它根據(jù)客戶端的IP地址來(lái)限流,并且設(shè)置了每秒可以處理的請(qǐng)求數(shù)(rate)為1。burst
參數(shù)定義了可以累積的最大請(qǐng)求數(shù),而nodelay
表示不對(duì)超出速率的請(qǐng)求進(jìn)行延遲處理。
按時(shí)間范圍限流:
如果你想在特定時(shí)間范圍內(nèi)限流,你可能需要編寫一些Lua腳本來(lái)實(shí)現(xiàn)這個(gè)邏輯。例如,你可以使用lua-resty-limit-traffic
庫(kù)的limit.req
模塊,并結(jié)合時(shí)間判斷邏輯:
local limit_req = require "resty.limit.req" local lim, err = limit_req.new("my_limit_req_store", 2, 0) if not lim then ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) -- 檢查當(dāng)前時(shí)間是否在限流時(shí)間范圍內(nèi) local current_hour = os.date("%H") if current_hour >= "09" and current_hour <= "18" then -- 在工作時(shí)間進(jìn)行限流 if delay then if delay >= 0.001 then -- 延遲處理 ngx.sleep(delay) end else if err == "rejected" then -- 請(qǐng)求超出速率限制 return ngx.exit(503) end ngx.log(ngx.ERR, "failed to limit req: ", err) return ngx.exit(500) end end
按來(lái)源IP限流:
使用ngx_http_limit_conn_module
模塊,可以限制同時(shí)處理的連接數(shù)。如果你想根據(jù)來(lái)源IP地址進(jìn)行限流,可以像這樣配置:
http { limit_conn_zone $binary_remote_addr zone=addr:10m; server { location / { limit_conn addr 3; } } }
這個(gè)配置限制了每個(gè)IP地址同時(shí)只能有3個(gè)活躍連接。
在實(shí)際部署時(shí),需要根據(jù)自己的業(yè)務(wù)需求調(diào)整這些配置參數(shù)。需要注意的是,對(duì)于復(fù)雜的限流規(guī)則,可能需要結(jié)合多個(gè)Nginx模塊和Lua腳本來(lái)實(shí)現(xiàn)。
而且,由于限流策略可能會(huì)影響用戶體驗(yàn),應(yīng)謹(jǐn)慎設(shè)計(jì)限流規(guī)則,確保它們既能保護(hù)后端服務(wù),又不會(huì)對(duì)合法用戶造成不必要的麻煩。
二、限流后提示信息處理和請(qǐng)求狀態(tài)
在OpenResty中,如果你使用了內(nèi)置的限流模塊(如ngx_http_limit_req_module
或ngx_http_limit_conn_module
)并且請(qǐng)求被限流,你可以通過(guò)返回特定的狀態(tài)碼和錯(cuò)誤頁(yè)面來(lái)通知用戶。
例如,如果使用limit_req
或limit_conn
指令,你可以設(shè)置返回503狀態(tài)碼(服務(wù)不可用),然后定義一個(gè)自定義的錯(cuò)誤頁(yè)面:
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; limit_conn_zone $binary_remote_addr zone=addr:10m; server { location / { limit_req zone=one burst=5 nodelay; limit_conn addr 3; error_page 503 /custom_503.html; } location = /custom_503.html { root /path/to/your/error/pages; internal; } } }
在上面的配置中,當(dāng)請(qǐng)求被限流并返回503狀態(tài)碼時(shí),Nginx將會(huì)發(fā)送/path/to/your/error/pages/custom_503.html
文件的內(nèi)容作為響應(yīng)。
如果你使用Lua腳本來(lái)處理限流,你可以更加靈活地設(shè)置返回的內(nèi)容。例如,你可以使用ngx.exit
來(lái)返回狀態(tài)碼,同時(shí)使用ngx.say
或ngx.send_headers
來(lái)發(fā)送自定義的響應(yīng)體或者響應(yīng)頭。
access_by_lua_block { -- 假設(shè)你已經(jīng)進(jìn)行了一些限流判斷... if should_limit then ngx.status = ngx.HTTP_SERVICE_UNAVAILABLE ngx.header.content_type = 'text/html' ngx.say("<html><body>Sorry, we are currently receiving too many requests. Please try again later.</body></html>") ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end }
在這個(gè)Lua代碼塊中,如果should_limit
變量為true
,則返回503狀態(tài)碼,并顯示一個(gè)自定義的HTML錯(cuò)誤消息。
要注意的是,返回給用戶的信息應(yīng)該既明確又友好,以確保用戶理解為什么他們的請(qǐng)求沒(méi)有成功,并且知道下一步該做什么。對(duì)于API服務(wù),通常返回一個(gè)JSON對(duì)象,包含錯(cuò)誤碼和錯(cuò)誤信息會(huì)更加合適:
access_by_lua_block { -- 假設(shè)你已經(jīng)進(jìn)行了一些限流判斷... if should_limit then ngx.status = ngx.HTTP_TOO_MANY_REQUESTS -- 429 Too Many Requests ngx.header.content_type = 'application/json' ngx.say([[{"error": "rate_limit", "error_description": "Too many requests. Please try again later."}]]) ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS) end }
在這個(gè)例子中,我們使用了429狀態(tài)碼(太多請(qǐng)求),這是一個(gè)更具體的狀態(tài)碼,用來(lái)表示客戶端發(fā)送的請(qǐng)求已經(jīng)超過(guò)了服務(wù)器愿意處理的頻率。
三、如何動(dòng)態(tài)更新限流策略,實(shí)時(shí)生效,不需要重啟Nginx
動(dòng)態(tài)更新限流策略而不重啟Nginx服務(wù),可以通過(guò)以下幾種方式實(shí)現(xiàn):
Lua共享字典(shared dictionaries):
OpenResty提供了共享內(nèi)存字典,這是一種在Nginx工作進(jìn)程之間共享數(shù)據(jù)的機(jī)制。你可以使用共享字典來(lái)存儲(chǔ)限流配置,并且在Lua代碼中動(dòng)態(tài)讀取這些配置。這樣,當(dāng)你更新了共享字典中的配置時(shí),不需要重啟Nginx,新的請(qǐng)求將會(huì)使用新的限流配置。
例如,你可以定義一個(gè)共享字典來(lái)存儲(chǔ)限流速率:
http { lua_shared_dict my_limit_req_store 10m; init_by_lua_block { local dict = ngx.shared.my_limit_req_store dict:set("rate", 1) -- 設(shè)置每秒請(qǐng)求數(shù)為1 } server { location / { access_by_lua_block { local dict = ngx.shared.my_limit_req_store local rate = dict:get("rate") -- 動(dòng)態(tài)獲取當(dāng)前的限流速率 -- 接下來(lái)使用這個(gè)rate值來(lái)進(jìn)行限流... } } } }
當(dāng)你需要更新限流策略時(shí),只需修改共享字典中的值即可。
OpenResty的控制API:
OpenResty提供了一個(gè)控制API,可以用來(lái)動(dòng)態(tài)地調(diào)整運(yùn)行時(shí)的Nginx配置。這個(gè)API可以通過(guò)Lua代碼來(lái)調(diào)用,從而實(shí)現(xiàn)不重啟服務(wù)的情況下更新配置。
外部配置服務(wù):
你可以將限流配置存儲(chǔ)在外部服務(wù)中,比如數(shù)據(jù)庫(kù)、配置文件或者分布式配置系統(tǒng)(如etcd、Consul)。然后在Nginx的Lua代碼中定期輪詢這些服務(wù),獲取最新的限流配置。
access_by_lua_block { local http = require "resty.http" local httpc = http.new() local res, err = httpc:request_uri("http://config-service/get_rate_limit", { method = "GET", headers = { ["Content-Type"] = "application/json", } }) if not res then ngx.log(ngx.ERR, "failed to request: ", err) return end local rate_limit = tonumber(res.body) if rate_limit then -- 應(yīng)用新的限流策略... end }
信號(hào)控制:
Nginx支持通過(guò)信號(hào)來(lái)進(jìn)行控制,例如重新加載配置文件(nginx -s reload
)。雖然這不是實(shí)時(shí)的,但是它不需要完全重啟Nginx進(jìn)程,只是重新加載配置文件。如果限流策略是通過(guò)Nginx配置文件中的參數(shù)來(lái)控制的,這是一個(gè)可行的方法。
選擇哪種方法取決于你的具體需求和環(huán)境。如果你需要非??焖俚馗屡渲茫⑶遗渲酶虏僮鞣浅nl繁,那么使用Lua共享字典或者外部配置服務(wù)可能是更好的選擇。如果配置更新不是很頻繁,使用信號(hào)控制來(lái)重新加載Nginx配置可能就足夠了。
到此這篇關(guān)于OpenResty中實(shí)現(xiàn)按QPS、時(shí)間范圍、來(lái)源IP進(jìn)行限流的文章就介紹到這了,更多相關(guān)OpenResty限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Linux中Nginx的防盜鏈和優(yōu)化的實(shí)現(xiàn)代碼
今天是周末小編在值班哈,很開(kāi)森,工作使我快樂(lè),本文重點(diǎn)給大家介紹Linux中Nginx的防盜鏈和優(yōu)化問(wèn)題及實(shí)現(xiàn)代碼,需要的朋友跟隨小編一起看看吧2021-06-06利用nginx實(shí)現(xiàn)動(dòng)靜分離的負(fù)載均衡集群實(shí)戰(zhàn)教程
這篇文章介紹了利用nginx實(shí)現(xiàn)動(dòng)靜分離的負(fù)載均衡集群實(shí)戰(zhàn),本次用到的操作系統(tǒng)及服務(wù),本次實(shí)驗(yàn)一共需要3臺(tái)服務(wù)器,一臺(tái)nginx做為負(fù)載均衡分發(fā)器和動(dòng)靜分離的分發(fā)器,兩臺(tái)apache做為后端服務(wù)器,使用nginx實(shí)現(xiàn)兩臺(tái)apache服務(wù)器的負(fù)載均衡和動(dòng)靜分離,需要的朋友可以參考下2023-03-03windows系統(tǒng)下安裝Nginx及簡(jiǎn)單使用過(guò)程
Nginx是一個(gè)很強(qiáng)大的高性能Web和反向代理服務(wù),也是一種輕量級(jí)的Web服務(wù)器,可以作為獨(dú)立的服務(wù)器部署網(wǎng)站,應(yīng)用非常廣泛,特別是現(xiàn)在前后端分離的情況下,這篇文章主要介紹了windows系統(tǒng)下安裝Nginx以及簡(jiǎn)單使用,需要的朋友可以參考下2024-04-04Nginx實(shí)現(xiàn)TCP端口的偵聽(tīng)及轉(zhuǎn)發(fā)操作步驟
這篇文章主要介紹了Nginx實(shí)現(xiàn)TCP端口的偵聽(tīng)及轉(zhuǎn)發(fā)的相關(guān)資料,文章介紹了如何使用Nginx進(jìn)行TCP代理(四層代理)來(lái)處理MQTT的集群需求,包括配置Nginx支持stream模塊、編寫TCP代理配置文件以及重新加載Nginx以應(yīng)用更改,需要的朋友可以參考下2024-11-11nginx實(shí)現(xiàn)反向代理出現(xiàn)502的問(wèn)題解決
本文主要介紹了nginx實(shí)現(xiàn)反向代理出現(xiàn)502的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08配置nginx訪問(wèn)本地靜態(tài)資源,本地圖片,視頻教程
文章介紹了如何配置Nginx以訪問(wèn)本地靜態(tài)資源、圖片和視頻,首先,進(jìn)入Nginx安裝目錄并打開(kāi)`nginx.conf`文件,添加一個(gè)新的`server`配置來(lái)指定本地路徑,然后,通過(guò)命令行重啟Nginx服務(wù)以應(yīng)用更改,最后,通過(guò)瀏覽器訪問(wèn)配置的圖片路徑來(lái)驗(yàn)證配置是否成功2025-01-01lnmp環(huán)境中如何為nginx開(kāi)啟pathinfo
這篇文章主要介紹了lnmp環(huán)境中如何為nginx開(kāi)啟pathinfo的方法,操作很簡(jiǎn)單,需要的朋友可以參考下2015-01-01Nginx的nginx.conf配置文件中文注釋說(shuō)明
這篇文章主要介紹了Nginx的nginx.conf配置文件中文注釋說(shuō)明,本文是個(gè)人注釋版,在生產(chǎn)環(huán)境中經(jīng)常使用,需要的朋友可以參考下2014-12-12