ASP.NET?Core中間件實現(xiàn)限流的代碼
一、限流算法
在高并發(fā)系統(tǒng)中,有三把利器用來保護系統(tǒng):緩存、降級和限流。
本文主要是介紹限流,限流算法主要有以下三種:
1.計數(shù)器算法
- 固定窗口
- 滑動窗口
2.令牌桶算法
3.漏桶算法
1.計數(shù)器算法
1.1 固定窗口算法
計數(shù)器算法是限流算法里最簡單也是最容易實現(xiàn)的一種算法。比如我們規(guī)定,對于A接口來說,我們1分鐘的訪問次數(shù)不能超過100個。那么我們可以這么做:在一開 始的時候,我們可以設(shè)置一個計數(shù)器counter,每當一個請求過來的時候,counter就加1,如果counter的值大于100并且該請求與第一個 請求的間隔時間還在1分鐘之內(nèi),那么說明請求數(shù)過多;如果該請求與第一個請求的間隔時間大于1分鐘,且counter的值還在限流范圍內(nèi),那么就重置 counter。
java中的具體實現(xiàn)如下:
public class CounterTest {
public long timeStamp = getNowTime();
public int reqCount = 0;
public final int limit = 100; // 時間窗口內(nèi)最大請求數(shù)
public final long interval = 1000; // 時間窗口ms
public boolean grant() {
long now = getNowTime();
if (now < timeStamp + interval) {
// 在時間窗口內(nèi)
reqCount++;
// 判斷當前時間窗口內(nèi)是否超過最大請求控制數(shù)
return reqCount <= limit;
} else {
timeStamp = now;
// 超時后重置
reqCount = 1;
return true;
}
}
public long getNowTime() {
return System.currentTimeMillis();
}
}.NET Core中的具體實現(xiàn)如下:
AspNetCoreRateLimit是目前ASP.NET Core下最常用的限流解決方案,AspNetCoreRateLimit的源碼實現(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è)有一個惡意用戶,他在0:59時,瞬間發(fā)送了100個請求,并且1:00又瞬間發(fā)送了100個請求,那么其實這個用戶在 1秒里面,瞬間發(fā)送了200個請求。我們剛才規(guī)定的是1分鐘最多100個請求,也就是每秒鐘最多1.7個請求,用戶通過在時間窗口的重置節(jié)點處突發(fā)請求, 可以瞬間超過我們的速率限制。用戶有可能通過算法的這個漏洞,瞬間壓垮我們的應(yīng)用。
1.2 滑動窗口算法
滑動窗口類似于固定窗口算法,但它通過將前一個窗口中的加權(quán)計數(shù)添加到當前窗口中的計數(shù)來計算估計數(shù),如果估計數(shù)超過計數(shù)限制,則請求將被阻止。
具體公式如下:
估計數(shù) = 前一窗口計數(shù) * (1 - 當前窗口經(jīng)過時間 / 單位時間) + 當前窗口計數(shù)

窗口[00:00, 00:01)中有9個請求,窗口[00:01, 00:02)中有5個請求。對于01:15到達的請求,即窗口[00:01, 00:02)的25%位置,通過公式計算請求計數(shù):9 x (1 - 25%) + 5 = 11.75 > 10. 因此我們拒絕此請求。
即使兩個窗口都沒有超過限制,請求也會被拒絕,因為前一個和當前窗口的加權(quán)和確實超過了限制。
2.令牌桶算法
令牌桶算法是比較常見的限流算法之一,大概描述如下:
1)所有的請求在處理之前都需要拿到一個可用的令牌才會被處理;
2)根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌;
3)桶設(shè)置最大的放置令牌限制,當桶滿時、新添加的令牌就被丟棄或者拒絕;
4)請求達到后首先要獲取令牌桶中的令牌,拿著令牌才可以進行其他的業(yè)務(wù)邏輯,處理完業(yè)務(wù)邏輯之后,將令牌直接刪除;
5)令牌桶有最低限額,當桶中的令牌達到最低限額的時候,請求處理完之后將不會刪除令牌,以此保證足夠的限流;

3.漏桶算法
漏桶算法其實很簡單,可以粗略的認為就是注水漏水過程,往桶中以一定速率流出水,以任意速率流入水,當水超過桶流量則丟棄,因為桶容量是不變的,保證了整體的速率。

二、ASP.NET Core中間件實現(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次請求相距2個窗口時間,則可以認為前一窗口計數(shù)為0,重新開始計數(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.在管道中的使用
需要注意的是,我們注冊Middleware時,必須使用單例模式,保證所有請求通過同一SlidingWindow計數(shù):
services.AddSingleton<RateLimitMiddleware>();
到此這篇關(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基于滑動窗口實現(xiàn)限流控制
- 解決ASP.NET?Core中使用漏桶算法限流的問題
- ASP.NET?Core中使用滑動窗口限流的問題及場景分析
- ASP.NET?Core使用固定窗口限流
- ASP.NET Core中使用令牌桶限流的實現(xiàn)
- Asp.NET Core 限流控制(AspNetCoreRateLimit)的實現(xiàn)
- ASP.NET Core對不同類型的用戶進行區(qū)別限流詳解
- 在Asp.netCore中使用Attribute來描述限流的操作步驟
相關(guān)文章
ASP.NET將文件寫到另一服務(wù)器(圖文教程)及注意事項
有時我們需要將來自于客戶端的文件上傳到WEB服務(wù)器端,并在服務(wù)端將文件存儲到第三方文件服務(wù)器中存儲,既然有需求,那就有實現(xiàn)了,感興趣的你可以了解此文,或許對你學(xué)習asp.net 起到很好的作用哦2013-01-01
異步 HttpContext.Current實現(xiàn)取值的方法(解決異步Application,Session,Cache.
在一個項目中,為了系統(tǒng)執(zhí)行效率更快,把一個經(jīng)常用到的數(shù)據(jù)庫表通過dataset放到Application中,發(fā)現(xiàn)在異步實現(xiàn)中每一次都會出現(xiàn)HttpContext.Current為null的異常,后來在網(wǎng)上查了好多資料,發(fā)現(xiàn)問這個問題的人多,回答的少2009-07-07
前兩天看了一個自定義分頁控件,和AspNetPager一樣是實現(xiàn)IPostBackEventHandler接口,不過簡潔許多,就想能不能實現(xiàn)ICallbackEventHandler接口做到無刷新分頁呢?想到了就馬上去做,終于,設(shè)想變成了現(xiàn)實?。?/div> 2010-03-03
ASP.NET Core文件壓縮常見使用誤區(qū)(最佳實踐)
本文給大家分享ASP.NET Core文件壓縮常見的三種誤區(qū),就每種誤區(qū)給大家講解的非常詳細,是項目實踐的最佳紀錄,對ASP.NET Core文件壓縮相關(guān)知識感興趣的朋友一起看看吧2021-05-05
.NET全局靜態(tài)可訪問IServiceProvider的過程詳解(支持Blazor)
為解決在靜態(tài)方法中訪問依賴注入(DI)容器的問題,提出了通過DependencyInjection.StaticAccessor包實現(xiàn)靜態(tài)訪問,這一方法特別適用于需要在靜態(tài)方法中獲取范圍內(nèi)(Scoped)服務(wù)的場景,感興趣的朋友跟隨小編一起看看吧2024-09-09最新評論

