ASP.NET?Core中間件實(shí)現(xiàn)限流的代碼
一、限流算法
在高并發(fā)系統(tǒng)中,有三把利器用來(lái)保護(hù)系統(tǒng):緩存、降級(jí)和限流。
本文主要是介紹限流,限流算法主要有以下三種:
1.計(jì)數(shù)器算法
- 固定窗口
- 滑動(dòng)窗口
2.令牌桶算法
3.漏桶算法
1.計(jì)數(shù)器算法
1.1 固定窗口算法
計(jì)數(shù)器算法是限流算法里最簡(jiǎn)單也是最容易實(shí)現(xiàn)的一種算法。比如我們規(guī)定,對(duì)于A接口來(lái)說(shuō),我們1分鐘的訪問(wèn)次數(shù)不能超過(guò)100個(gè)。那么我們可以這么做:在一開(kāi) 始的時(shí)候,我們可以設(shè)置一個(gè)計(jì)數(shù)器counter,每當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)的時(shí)候,counter就加1,如果counter的值大于100并且該請(qǐng)求與第一個(gè) 請(qǐng)求的間隔時(shí)間還在1分鐘之內(nèi),那么說(shuō)明請(qǐng)求數(shù)過(guò)多;如果該請(qǐng)求與第一個(gè)請(qǐng)求的間隔時(shí)間大于1分鐘,且counter的值還在限流范圍內(nèi),那么就重置 counter。
java中的具體實(shí)現(xiàn)如下:
public class CounterTest { public long timeStamp = getNowTime(); public int reqCount = 0; public final int limit = 100; // 時(shí)間窗口內(nèi)最大請(qǐng)求數(shù) public final long interval = 1000; // 時(shí)間窗口ms public boolean grant() { long now = getNowTime(); if (now < timeStamp + interval) { // 在時(shí)間窗口內(nèi) reqCount++; // 判斷當(dāng)前時(shí)間窗口內(nèi)是否超過(guò)最大請(qǐng)求控制數(shù) return reqCount <= limit; } else { timeStamp = now; // 超時(shí)后重置 reqCount = 1; return true; } } public long getNowTime() { return System.currentTimeMillis(); } }
.NET Core中的具體實(shí)現(xiàn)如下:
AspNetCoreRateLimit是目前ASP.NET Core下最常用的限流解決方案,AspNetCoreRateLimit的源碼實(shí)現(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 }; } }
固定窗口算法缺點(diǎn)
從上圖中我們可以看到,假設(shè)有一個(gè)惡意用戶,他在0:59時(shí),瞬間發(fā)送了100個(gè)請(qǐng)求,并且1:00又瞬間發(fā)送了100個(gè)請(qǐng)求,那么其實(shí)這個(gè)用戶在 1秒里面,瞬間發(fā)送了200個(gè)請(qǐng)求。我們剛才規(guī)定的是1分鐘最多100個(gè)請(qǐng)求,也就是每秒鐘最多1.7個(gè)請(qǐng)求,用戶通過(guò)在時(shí)間窗口的重置節(jié)點(diǎn)處突發(fā)請(qǐng)求, 可以瞬間超過(guò)我們的速率限制。用戶有可能通過(guò)算法的這個(gè)漏洞,瞬間壓垮我們的應(yīng)用。
1.2 滑動(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ù)
窗口[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ò)了限制。
2.令牌桶算法
令牌桶算法是比較常見(jiàn)的限流算法之一,大概描述如下:
1)所有的請(qǐng)求在處理之前都需要拿到一個(gè)可用的令牌才會(huì)被處理;
2)根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌;
3)桶設(shè)置最大的放置令牌限制,當(dāng)桶滿時(shí)、新添加的令牌就被丟棄或者拒絕;
4)請(qǐng)求達(dá)到后首先要獲取令牌桶中的令牌,拿著令牌才可以進(jìn)行其他的業(yè)務(wù)邏輯,處理完業(yè)務(wù)邏輯之后,將令牌直接刪除;
5)令牌桶有最低限額,當(dāng)桶中的令牌達(dá)到最低限額的時(shí)候,請(qǐng)求處理完之后將不會(huì)刪除令牌,以此保證足夠的限流;
3.漏桶算法
漏桶算法其實(shí)很簡(jiǎn)單,可以粗略的認(rèn)為就是注水漏水過(guò)程,往桶中以一定速率流出水,以任意速率流入水,當(dāng)水超過(guò)桶流量則丟棄,因?yàn)橥叭萘渴遣蛔兊?,保證了整體的速率。
二、ASP.NET Core中間件實(shí)現(xiàn)限流
1.中間件代碼
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; 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ù)。
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); } }
2.在管道中的使用
需要注意的是,我們注冊(cè)Middleware時(shí),必須使用單例模式,保證所有請(qǐng)求通過(guò)同一SlidingWindow計(jì)數(shù):
services.AddSingleton<RateLimitMiddleware>();
到此這篇關(guān)于ASP.NET Core中間件-限流的文章就介紹到這了,更多相關(guān)ASP.NET Core中間件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Asp.Net?Core7?preview4限流中間件新特性詳解
- ASP.NET?Core設(shè)置Ocelot網(wǎng)關(guān)限流
- ASP.NET?Core基于滑動(dòng)窗口實(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將文件寫(xiě)到另一服務(wù)器(圖文教程)及注意事項(xiàng)
有時(shí)我們需要將來(lái)自于客戶端的文件上傳到WEB服務(wù)器端,并在服務(wù)端將文件存儲(chǔ)到第三方文件服務(wù)器中存儲(chǔ),既然有需求,那就有實(shí)現(xiàn)了,感興趣的你可以了解此文,或許對(duì)你學(xué)習(xí)asp.net 起到很好的作用哦2013-01-01異步 HttpContext.Current實(shí)現(xiàn)取值的方法(解決異步Application,Session,Cache.
在一個(gè)項(xiàng)目中,為了系統(tǒng)執(zhí)行效率更快,把一個(gè)經(jīng)常用到的數(shù)據(jù)庫(kù)表通過(guò)dataset放到Application中,發(fā)現(xiàn)在異步實(shí)現(xiàn)中每一次都會(huì)出現(xiàn)HttpContext.Current為null的異常,后來(lái)在網(wǎng)上查了好多資料,發(fā)現(xiàn)問(wèn)這個(gè)問(wèn)題的人多,回答的少2009-07-07asp.net 無(wú)刷新翻頁(yè)就是這么簡(jiǎn)單
前兩天看了一個(gè)自定義分頁(yè)控件,和AspNetPager一樣是實(shí)現(xiàn)IPostBackEventHandler接口,不過(guò)簡(jiǎn)潔許多,就想能不能實(shí)現(xiàn)ICallbackEventHandler接口做到無(wú)刷新分頁(yè)呢?想到了就馬上去做,終于,設(shè)想變成了現(xiàn)實(shí)??!2010-03-03ASP.NET Core文件壓縮常見(jiàn)使用誤區(qū)(最佳實(shí)踐)
本文給大家分享ASP.NET Core文件壓縮常見(jiàn)的三種誤區(qū),就每種誤區(qū)給大家講解的非常詳細(xì),是項(xiàng)目實(shí)踐的最佳紀(jì)錄,對(duì)ASP.NET Core文件壓縮相關(guān)知識(shí)感興趣的朋友一起看看吧2021-05-05Web API中使用Autofac實(shí)現(xiàn)依賴注入
本文詳細(xì)講解了Web API中使用Autofac實(shí)現(xiàn)依賴注入的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03.NET全局靜態(tài)可訪問(wèn)IServiceProvider的過(guò)程詳解(支持Blazor)
為解決在靜態(tài)方法中訪問(wèn)依賴注入(DI)容器的問(wèn)題,提出了通過(guò)DependencyInjection.StaticAccessor包實(shí)現(xiàn)靜態(tài)訪問(wèn),這一方法特別適用于需要在靜態(tài)方法中獲取范圍內(nèi)(Scoped)服務(wù)的場(chǎng)景,感興趣的朋友跟隨小編一起看看吧2024-09-09.NET Core系列之MemoryCache 緩存選項(xiàng)
這篇文章主要介紹了.NET Core系列之MemoryCache 緩存選項(xiàng),詳細(xì)的介紹一下 MSCache 中的 Options,由此來(lái)介紹一些 MSCache 中的內(nèi)部機(jī)制,感興趣的小伙伴們可以參考一下2018-08-08