ASP.NET?Core基于滑動(dòng)窗口實(shí)現(xiàn)限流控制
前言:
在實(shí)際項(xiàng)目中,為了保障服務(wù)器的穩(wěn)定運(yùn)行,需要對(duì)接口的可訪問(wèn)頻次進(jìn)行限流控制,避免因客戶端頻繁請(qǐng)求導(dǎo)致服務(wù)器壓力過(guò)大。
而?AspNetCoreRateLimit
是目前ASP.NET Core
下最常用的限流解決方案。
查看它的實(shí)現(xiàn)代碼,我發(fā)現(xiàn)它使用的固定窗口算法。
var entry = await _counterStore.GetAsync(counterId, cancellationToken); if (entry.HasValue) { ? ? // entry has not expired ? ? if (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow) ? ? { ? ? ? ? // increment request count ? ? ? ? var totalCount = entry.Value.Count + _config.RateIncrementer?.Invoke() ?? 1; ? ? ? ? // deep copy ? ? ? ? counter = new RateLimitCounter ? ? ? ? { ? ? ? ? ? ? Timestamp = entry.Value.Timestamp, ? ? ? ? ? ? Count = totalCount ? ? ? ? }; ? ? } }
二、固定窗口算法
固定窗口算法是將時(shí)間線劃分為固定大小的窗口,并為每個(gè)窗口分配一個(gè)計(jì)數(shù)器。每個(gè)請(qǐng)求,根據(jù)其到達(dá)時(shí)間,被映射到一個(gè)窗口。如果窗口中的計(jì)數(shù)器已達(dá)到限制,則拒絕落在此窗口中的請(qǐng)求。
例如,如果我們將窗口大小設(shè)置為one分鐘,每分鐘允許ten個(gè)請(qǐng)求:
ASP.NET Core
基于滑動(dòng)窗口算法實(shí)現(xiàn)限流控制 #yyds干貨盤點(diǎn)#_滑動(dòng)窗口
59秒的請(qǐng)求將被阻止,因?yàn)檫@時(shí)已經(jīng)接受了10個(gè)請(qǐng)求。1分鐘時(shí)計(jì)數(shù)器歸零,所以1分01秒的請(qǐng)求可以接受。
??固定窗口算法的問(wèn)題主要在于,如果在窗口邊緣發(fā)生大量請(qǐng)求,會(huì)導(dǎo)致限流策略失效。??
比如,在59秒接收了9個(gè)請(qǐng)求,在1分01秒又可以再接收10個(gè)請(qǐng)求,相當(dāng)于每分鐘允許了20個(gè)請(qǐng)求。
三、滑動(dòng)窗口算法
滑動(dòng)窗口類似于固定窗口算法,但它通過(guò)將前一個(gè)窗口中的加權(quán)計(jì)數(shù)添加到當(dāng)前窗口中的計(jì)數(shù)來(lái)計(jì)算估計(jì)數(shù),如果估計(jì)數(shù)超過(guò)計(jì)數(shù)限制,則請(qǐng)求將被阻止。
具體公式如下:
估計(jì)數(shù) = 前一窗口計(jì)數(shù) * (1 - 當(dāng)前窗口經(jīng)過(guò)時(shí)間 / 單位時(shí)間) + 當(dāng)前窗口計(jì)數(shù)
例如,假設(shè)限制為每分鐘10個(gè):
窗口[00:00, 00:01)中有9個(gè)請(qǐng)求,窗口[00:01, 00:02)中有5個(gè)請(qǐng)求。對(duì)于01:15到達(dá)的請(qǐng)求,即窗口[00:01, 00:02)的25%位置,通過(guò)公式計(jì)算請(qǐng)求計(jì)數(shù):9 x (1 - 25%) + 5 = 11.75 > 10. 因此我們拒絕此請(qǐng)求。
??即使兩個(gè)窗口都沒(méi)有超過(guò)限制,請(qǐng)求也會(huì)被拒絕,因?yàn)榍耙粋€(gè)和當(dāng)前窗口的加權(quán)和確實(shí)超過(guò)了限制。??
四、實(shí)現(xiàn)
根據(jù)上面的公式,實(shí)現(xiàn)滑動(dòng)窗口算法代碼如下:
public class SlidingWindow { ? ? private readonly object _syncObject = new object(); ? ? private readonly int _requestIntervalSeconds; ? ? private readonly int _requestLimit; ? ? private DateTime _windowStartTime; ? ? private int _prevRequestCount; ? ? private int _requestCount; ? ? public SlidingWindow(int requestLimit, int requestIntervalSeconds) ? ? { ? ? ? ? _windowStartTime = DateTime.Now; ? ? ? ? _requestLimit = requestLimit; ? ? ? ? _requestIntervalSeconds = requestIntervalSeconds; ? ? } ? ? public bool PassRequest() ? ? { ? ? ? ? lock (_syncObject) ? ? ? ? { ? ? ? ? ? ? var currentTime = DateTime.Now; ? ? ? ? ? ? var elapsedSeconds = (currentTime - _windowStartTime).TotalSeconds; ? ? ? ? ? ? if (elapsedSeconds >= _requestIntervalSeconds * 2) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? _windowStartTime = currentTime; ? ? ? ? ? ? ? ? _prevRequestCount = 0; ? ? ? ? ? ? ? ? _requestCount = 0; ? ? ? ? ? ? ? ? elapsedSeconds = 0; ? ? ? ? ? ? } ? ? ? ? ? ? else if (elapsedSeconds >= _requestIntervalSeconds) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? _windowStartTime = _windowStartTime.AddSeconds(_requestIntervalSeconds); ? ? ? ? ? ? ? ? _prevRequestCount = _requestCount; ? ? ? ? ? ? ? ? _requestCount = 0; ? ? ? ? ? ? ? ? elapsedSeconds = (currentTime - _windowStartTime).TotalSeconds; ? ? ? ? ? ? }? ? ? ? ? ? ? var requestCount = _prevRequestCount * (1 - elapsedSeconds / _requestIntervalSeconds) + _requestCount + 1; ? ? ? ? ? ? if (requestCount <= _requestLimit) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? _requestCount++; ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return false; ? ? } }
如果最近的2次請(qǐng)求相距2個(gè)窗口時(shí)間,則可以認(rèn)為前一窗口計(jì)數(shù)為0,重新開(kāi)始計(jì)數(shù)。
六、使用
新建Middleware,使用滑動(dòng)窗口算法進(jìn)行限流:
public class RateLimitMiddleware : IMiddleware { ? ? private readonly SlidingWindow _window; ? ? public RateLimitMiddleware() ? ? { ? ? ? ? _window = new SlidingWindow(10, 60); ? ? } ? ? public async Task InvokeAsync(HttpContext context, RequestDelegate next) ? ? { ? ? ? ? if (!_window.PassRequest()) ? ? ? ? { ? ? ? ? ? ? context.SetEndpoint(new Endpoint((context) => ? ? ? ? ? ? { ? ? ? ? ? ? ? ? context.Response.StatusCode = StatusCodes.Status403Forbidden; ? ? ? ? ? ? ? ? return Task.CompletedTask; ? ? ? ? ? ? }, ? ? ? ? ? ? ? ? ? ? ? ? EndpointMetadataCollection.Empty, ? ? ? ? ? ? ? ? ? ? ? ? "限流")); ? ? ? ? } ? ? ? ? await next(context); ? ? } }
??需要注意的是,我們注冊(cè)Middleware
時(shí),必須使用單例模式,保證所有請(qǐng)求通過(guò)同一SlidingWindow計(jì)數(shù):??
services.AddSingleton<RateLimitMiddleware>();
結(jié)論:
使用滑動(dòng)窗口算法,可以有效避免固定窗口算法存在的窗口邊緣大量請(qǐng)求無(wú)法限制的問(wèn)題。
到此這篇關(guān)于ASP.NET Core基于滑動(dòng)窗口實(shí)現(xiàn)限流控制的文章就介紹到這了,更多相關(guān)ASP.NET Core基于滑動(dòng)窗口實(shí)現(xiàn)限流控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(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中使用漏桶算法限流的問(wèn)題
- ASP.NET?Core中使用滑動(dòng)窗口限流的問(wèn)題及場(chǎng)景分析
- ASP.NET?Core使用固定窗口限流
- ASP.NET Core中使用令牌桶限流的實(shí)現(xiàn)
- Asp.NET Core 限流控制(AspNetCoreRateLimit)的實(shí)現(xiàn)
- ASP.NET Core對(duì)不同類型的用戶進(jìn)行區(qū)別限流詳解
- 在Asp.netCore中使用Attribute來(lái)描述限流的操作步驟
相關(guān)文章
ASP.NET操作Word的IIS權(quán)限設(shè)置
檢索 COM 類工廠中 CLSID 為 {00024500-0000-0000-C000-000000000046} 的組件時(shí)失敗,原因是出現(xiàn)以下錯(cuò)誤: 80070005。2011-02-02簡(jiǎn)單使用BackgroundWorker創(chuàng)建多個(gè)線程的教程
簡(jiǎn)單使用BackgroundWorker創(chuàng)建多個(gè)線程的教程,需要的朋友可以參考一下2013-03-03詳解ASP.NET Core WebApi 返回統(tǒng)一格式參數(shù)
這篇文章主要介紹了詳解ASP.NET Core WebApi 返回統(tǒng)一格式參數(shù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11ASP.NET中實(shí)現(xiàn)Form表單字段值自動(dòng)填充到操作模型中
這篇文章主要介紹了ASP.NET中實(shí)現(xiàn)Form表單字段值自動(dòng)填充到操作模型中,本文模仿MVC模式中的自動(dòng)映射表單了模型,使用泛型和反射實(shí)現(xiàn),需要的朋友可以參考下2015-06-06ASP.NET設(shè)計(jì)網(wǎng)絡(luò)硬盤之兩重要類代碼
要進(jìn)行“網(wǎng)絡(luò)硬盤”功能設(shè)計(jì),首先要熟悉.NET中處理文件和文件夾的操作。File類和Directory類是其中最主要的兩個(gè)類。了解它們將對(duì)后面功能的實(shí)現(xiàn)提供很大的便利2012-10-10gridview行索引獲取方法及實(shí)現(xiàn)代碼
GridView行索引的獲取有利于對(duì)GridView行數(shù)據(jù)進(jìn)行操作(刪、改)等等,接下來(lái)介紹獲取方法,感興趣的朋友可以了解下,閱讀本文希望對(duì)你有幫助2013-01-01