日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

ASP.NET?Core基于滑動窗口實現限流控制_實用技巧

作者:My?IO ? 更新時間: 2022-05-03 編程語言

前言:

在實際項目中,為了保障服務器的穩定運行,需要對接口的可訪問頻次進行限流控制,避免因客戶端頻繁請求導致服務器壓力過大。

而?AspNetCoreRateLimit是目前ASP.NET Core下最常用的限流解決方案。

查看它的實現代碼,我發現它使用的固定窗口算法。

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
? ? ? ? };
? ? }
}

二、固定窗口算法

固定窗口算法是將時間線劃分為固定大小的窗口,并為每個窗口分配一個計數器。每個請求,根據其到達時間,被映射到一個窗口。如果窗口中的計數器已達到限制,則拒絕落在此窗口中的請求。

例如,如果我們將窗口大小設置為one分鐘,每分鐘允許ten個請求:

ASP.NET Core基于滑動窗口算法實現限流控制 #yyds干貨盤點#_滑動窗口

59秒的請求將被阻止,因為這時已經接受了10個請求。1分鐘時計數器歸零,所以1分01秒的請求可以接受。

??固定窗口算法的問題主要在于,如果在窗口邊緣發生大量請求,會導致限流策略失效。??

比如,在59秒接收了9個請求,在1分01秒又可以再接收10個請求,相當于每分鐘允許了20個請求。

三、滑動窗口算法

滑動窗口類似于固定窗口算法,但它通過將前一個窗口中的加權計數添加到當前窗口中的計數來計算估計數,如果估計數超過計數限制,則請求將被阻止。

具體公式如下:

估計數 = 前一窗口計數 * (1 - 當前窗口經過時間 / 單位時間) + 當前窗口計數

例如,假設限制為每分鐘10個:

窗口[00:00, 00:01)中有9個請求,窗口[00:01, 00:02)中有5個請求。對于01:15到達的請求,即窗口[00:01, 00:02)的25%位置,通過公式計算請求計數:9 x (1 - 25%) + 5 = 11.75 > 10. 因此我們拒絕此請求。

??即使兩個窗口都沒有超過限制,請求也會被拒絕,因為前一個和當前窗口的加權和確實超過了限制。??

四、實現

根據上面的公式,實現滑動窗口算法代碼如下:

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次請求相距2個窗口時間,則可以認為前一窗口計數為0,重新開始計數。

六、使用

新建Middleware,使用滑動窗口算法進行限流:

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);
? ? }
}

??需要注意的是,我們注冊Middleware時,必須使用單例模式,保證所有請求通過同一SlidingWindow計數:??

services.AddSingleton();

結論:

使用滑動窗口算法,可以有效避免固定窗口算法存在的窗口邊緣大量請求無法限制的問題。

原文鏈接:https://blog.51cto.com/MyIO/5056224

欄目分類
最近更新