ASP.NET?Core中使用滑動窗口限流的問題及場景分析
滑動窗口算法用于應(yīng)對請求在時間周期中分布不均勻的情況,能夠更精確的應(yīng)對流量變化,比較著名的應(yīng)用場景就是TCP協(xié)議的流量控制,不過今天要說的是服務(wù)限流場景中的應(yīng)用。
算法原理
這里假設(shè)業(yè)務(wù)需要每秒鐘限流100次,先來看固定窗口算法的兩個問題:
漏檢
如下圖所示,單看第1秒和第2秒,其請求次數(shù)都沒有超過100,所以使用固定窗口算法時不會觸發(fā)限流。但是第1秒的后500ms的請求數(shù)加上第2秒的前500毫秒的請求數(shù)就超過了100,這時候可能會給系統(tǒng)帶來傷害,使用固定窗口算法時不能檢測到這種情況。
太剛
針對漏檢的問題,你可能會說,可以把時間窗口設(shè)置為500ms,把限流閾值設(shè)置為50。那么來看下圖,除了第2個計數(shù)周期超過了50,從而觸發(fā)限流,前后幾個計數(shù)周期的請求都很正常,甚至都不會超過閾值的50%,可能第2個計數(shù)周期的情況實(shí)在太特殊,1天都不會出現(xiàn)第2次,如果對系統(tǒng)不會造成影響,能不能通融下,做不到!固定窗口算法這時候就會顯得太過剛性。
那么滑動窗口如何來解決這兩個問題呢?還是先來看圖:
如上圖所示:
- 滑動窗口的時間跨度是1秒,每個小計數(shù)周期的時間跨度是500ms,此處的滑動窗口包含2個小計數(shù)周期。
- 隨著時間的前進(jìn),滑動窗口包含的小計數(shù)周期會以500ms為單位向前移動,但始終是包含2個小計數(shù)周期。
- 判斷是否限流時,需要將當(dāng)前滑動窗口包含的2個小計數(shù)周期的計數(shù)值加起來。
- 相比固定窗口計數(shù)器算法,滑動窗口可以有效減少漏檢,如上圖滑動窗口移動到了500-1500ms,發(fā)現(xiàn)總數(shù)超過100,則觸發(fā)限流;滑動窗口在0-1000ms、1000-2000ms時都不會觸發(fā)限流,即使其中某個小周期的計數(shù)值超過了閾值的半數(shù),但是總數(shù)沒有超過100,就不會限流,能夠應(yīng)對極少出現(xiàn)的突發(fā)流量情況。
從分析還可以看出,滑動窗口的小周期劃分的越多,則檢測越準(zhǔn)確,但用于跟蹤的計數(shù)也越多,使用的內(nèi)存和計算量都會增大。
算法實(shí)現(xiàn)
這里講兩種實(shí)現(xiàn)方法:進(jìn)程內(nèi)即內(nèi)存滑動窗口算法、基于Redis的滑動窗口算法。
進(jìn)程內(nèi)即內(nèi)存滑動窗口算法
這里介紹一種性能比較高的方法,使用數(shù)組實(shí)現(xiàn)滑動窗口,這是環(huán)形隊(duì)列的一種特例,如下圖所示:
- 假設(shè)滑動窗口需要5個小的計數(shù)周期,則初始化一個長度為5的整形數(shù)組,數(shù)字表示數(shù)組中的第幾個元素。
- 我們知道隊(duì)列有頭有尾,從隊(duì)頭取出數(shù)據(jù),向隊(duì)尾插入數(shù)據(jù),帶括號的數(shù)字表示是隊(duì)列中的第幾個元素。
- 滑動窗口向前移動時,隊(duì)尾向右移動1位,同時隊(duì)頭也向右移動1位。
- 隊(duì)尾和隊(duì)頭向右移動都可能會溢出數(shù)組,此時讓它們回到數(shù)組的起始位置,即圖中數(shù)組的第1個位置。
關(guān)于這個算法的詳細(xì)介紹,可以看這篇文章:www.dbjr.com.cn/article/200672.htm
基于Redis的滑動窗口算法
基于Redis時也可以使用類似環(huán)形隊(duì)列的方法,比如定義5個KV作為數(shù)組的5個元素。不過我之前實(shí)現(xiàn)時采用了一種更直觀的方式,每個小的計數(shù)周期都創(chuàng)建一個KV,同時設(shè)置一個絕對超過滑動窗口時間跨度的過期時間,用不到的小計數(shù)周期不會一直占用內(nèi)存;判斷是否觸發(fā)限流時,把這些小滑動窗口的計數(shù)值累加起來就可以了。當(dāng)然實(shí)際實(shí)現(xiàn)時還需要完善一些細(xì)節(jié)上的處理,比如怎么找到這些小計數(shù)周期,會有多種方案,存起來或者臨時計算都可以。
這些操作邏輯可以封裝在一個Lua script中,因?yàn)長ua script在Redis中執(zhí)行時也是原子操作,所以Redis的限流計數(shù)在分布式部署時天然就是準(zhǔn)確的。
應(yīng)用算法
這里以限流組件 FireflySoft.RateLimit 為例,實(shí)現(xiàn)ASP.NET Core中的滑動窗口限流。
1、安裝Nuget包
有多種安裝方式,選擇自己喜歡的就行了。
包管理器命令:
Install-Package FireflySoft.RateLimit.AspNetCore
或者.NET命令:
dotnet add package FireflySoft.RateLimit.AspNetCore
或者項(xiàng)目文件直接添加:
<ItemGroup> <PackageReference Include="FireflySoft.RateLimit.AspNetCore" Version="2.*" /> </ItemGroup>
2、使用中間件
在Startup中使用中間件,演示代碼如下(下邊會有詳細(xì)說明):
public void ConfigureServices(IServiceCollection services) { ... app.AddRateLimit(new InProcessSlidingWindowAlgorithm( new[] { // 構(gòu)造函數(shù)有兩個參數(shù):滑動窗口的時間長度、小計數(shù)周期的時間長度 new SlidingWindowRule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1)) { ExtractTarget = context => { // 提取限流目標(biāo) return (context as HttpContext).Request.Path.Value; }, CheckRuleMatching = context => { // 判斷當(dāng)前請求是否需要限流處理 return true; }, Name="sliding window limit rule", LimitNumber=100, // 限流閾值,這里即5秒最多100次請求 } }) ); ... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { ... app.UseRateLimit(); ... }
如上需要先注冊服務(wù),然后使用中間件。
注冊服務(wù)的時候需要提供限流算法和對應(yīng)的規(guī)則:
- 這里使用進(jìn)程內(nèi)固定窗口算法InProcessSlidingWindowAlgorithm,還可以使用RedisSlidingWindowAlgorithm,需要傳入一個Redis連接。兩種算法都支持同步和異步方法。
- 限流閾值是100,限流滑動窗口是5秒,小計數(shù)周期是1秒。
- ExtractTarget用于提取限流目標(biāo),這里是每個不同的請求Path。如果有IO請求,這里還支持對應(yīng)的異步方法ExtractTargetAsync。
- CheckRuleMatching用于驗(yàn)證當(dāng)前請求是否限流。如果有IO請求,這里還支持對應(yīng)的異步方法CheckRuleMatchingAsync。
- 默認(rèn)被限流時會返回HttpStatusCode 429,可以在AddRateLimit時使用可選參數(shù)error自定義這個值,以及Http Header和Body中的內(nèi)容。
基本的使用就是上邊例子中的這些了。
如果還是基于傳統(tǒng)的.NET Framework,則需要在Application_Start中注冊一個消息處理器RateLimitHandler,算法和規(guī)則部分都是共用的,具體可以看Github上的使用說明:https://github.com/bosima/FireflySoft.RateLimit
FireflySoft.RateLimit 是一個基于 .NET Standard 的限流類庫,其內(nèi)核簡單輕巧,能夠靈活應(yīng)對各種需求的限流場景。
其主要特點(diǎn)包括:
- 多種限流算法:內(nèi)置固定窗口、滑動窗口、漏桶、令牌桶四種算法,還可自定義擴(kuò)展。
- 多種計數(shù)存儲:目前支持內(nèi)存、Redis兩種存儲方式。
- 分布式友好:通過Redis存儲支持分布式程序統(tǒng)一計數(shù)。
- 限流目標(biāo)靈活:可以從請求中提取各種數(shù)據(jù)用于設(shè)置限流目標(biāo)。
- 支持限流懲罰:可以在客戶端觸發(fā)限流后鎖定一段時間不允許其訪問。
- 動態(tài)更改規(guī)則:支持程序運(yùn)行時動態(tài)更改限流規(guī)則。
- 自定義錯誤:可以自定義觸發(fā)限流后的錯誤碼和錯誤消息。
- 普適性:原則上可以滿足任何需要限流的場景。
Github開源地址:https://github.com/bosima/FireflySoft.RateLimit
到此這篇關(guān)于ASP.NET?Core中使用滑動窗口限流的文章就介紹到這了,更多相關(guān)ASP.NET?Core限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Asp.Net?Core7?preview4限流中間件新特性詳解
- ASP.NET?Core設(shè)置Ocelot網(wǎng)關(guān)限流
- ASP.NET?Core基于滑動窗口實(shí)現(xiàn)限流控制
- ASP.NET?Core中間件實(shí)現(xiàn)限流的代碼
- 解決ASP.NET?Core中使用漏桶算法限流的問題
- ASP.NET?Core使用固定窗口限流
- ASP.NET Core中使用令牌桶限流的實(shí)現(xiàn)
- Asp.NET Core 限流控制(AspNetCoreRateLimit)的實(shí)現(xiàn)
- ASP.NET Core對不同類型的用戶進(jìn)行區(qū)別限流詳解
- 在Asp.netCore中使用Attribute來描述限流的操作步驟
相關(guān)文章
ASP.NET中實(shí)現(xiàn)根據(jù)匿名類、datatable、sql生成實(shí)體類
這篇文章主要介紹了ASP.NET中實(shí)現(xiàn)根據(jù)匿名類、datatable、sql生成實(shí)體類,這個小小工具類非常實(shí)用,使用起來也很方便,需要的朋友可以參考下2015-06-06ASP.NET?Core中的策略授權(quán)和ABP授權(quán)
這篇文章介紹了ASP.NET?Core中的策略授權(quán)和ABP授權(quán),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-02-02.Net使用RabbitMQ實(shí)現(xiàn)短信密碼重置的操作步驟
在C#開發(fā)中,通過RabbitMQ實(shí)現(xiàn)短信服務(wù)可增強(qiáng)應(yīng)用的消息通知能力,本文介紹了使用RabbitMQ發(fā)送短信的步驟,包括安裝RabbitMQ客戶端庫、創(chuàng)建連接和通道、實(shí)現(xiàn)短信發(fā)送服務(wù)、配置RabbitMQ消費(fèi)者,并集成到用戶密碼重置流程中,通過示例代碼,可以快速理解整個實(shí)現(xiàn)過程2024-09-09.Net Core 之 Ubuntu 14.04 部署過程(圖文詳解)
本篇文章主要介紹了.Net Core 之 Ubuntu 14.04 部署過程(圖文詳解),有興趣的可以了解一下。2016-11-11在Asp.net網(wǎng)頁上寫讀Cookie的兩種不同語法介紹
asp.net開發(fā)時,為了存儲一些信息通常是Session與Cookie同時使用,本文將會補(bǔ)充一下Cookie相關(guān)的資料,感興趣的朋友可以了解一下在網(wǎng)頁上寫讀Cookie的實(shí)現(xiàn),希望本文對你有所幫助2013-01-01