網(wǎng)站首頁 編程語言 正文
一、限流算法
在高并發(fā)系統(tǒng)中,有三把利器用來保護系統(tǒng):緩存、降級和限流。
本文主要是介紹限流,限流算法主要有以下三種:
1.計數(shù)器算法
- 固定窗口
- 滑動窗口
2.令牌桶算法
3.漏桶算法
1.計數(shù)器算法
1.1 固定窗口算法
計數(shù)器算法是限流算法里最簡單也是最容易實現(xiàn)的一種算法。比如我們規(guī)定,對于A接口來說,我們1分鐘的訪問次數(shù)不能超過100個。那么我們可以這么做:在一開 始的時候,我們可以設(shè)置一個計數(shù)器counter,每當(dāng)一個請求過來的時候,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++; // 判斷當(dāng)前時間窗口內(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ù)添加到當(dāng)前窗口中的計數(shù)來計算估計數(shù),如果估計數(shù)超過計數(shù)限制,則請求將被阻止。
具體公式如下:
估計數(shù) = 前一窗口計數(shù) * (1 - 當(dāng)前窗口經(jīng)過時間 / 單位時間) + 當(dāng)前窗口計數(shù)
窗口[00:00, 00:01)中有9個請求,窗口[00:01, 00:02)中有5個請求。對于01:15到達(dá)的請求,即窗口[00:01, 00:02)的25%位置,通過公式計算請求計數(shù):9 x (1 - 25%) + 5 = 11.75 > 10. 因此我們拒絕此請求。
即使兩個窗口都沒有超過限制,請求也會被拒絕,因為前一個和當(dāng)前窗口的加權(quán)和確實超過了限制。
2.令牌桶算法
令牌桶算法是比較常見的限流算法之一,大概描述如下:
1)所有的請求在處理之前都需要拿到一個可用的令牌才會被處理;
2)根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌;
3)桶設(shè)置最大的放置令牌限制,當(dāng)桶滿時、新添加的令牌就被丟棄或者拒絕;
4)請求達(dá)到后首先要獲取令牌桶中的令牌,拿著令牌才可以進行其他的業(yè)務(wù)邏輯,處理完業(yè)務(wù)邏輯之后,將令牌直接刪除;
5)令牌桶有最低限額,當(dāng)桶中的令牌達(dá)到最低限額的時候,請求處理完之后將不會刪除令牌,以此保證足夠的限流;
3.漏桶算法
漏桶算法其實很簡單,可以粗略的認(rèn)為就是注水漏水過程,往桶中以一定速率流出水,以任意速率流入水,當(dāng)水超過桶流量則丟棄,因為桶容量是不變的,保證了整體的速率。
二、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個窗口時間,則可以認(rèn)為前一窗口計數(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();
原文鏈接:https://blog.csdn.net/aa2528877987/article/details/123197556
相關(guān)推薦
- 2022-08-10 Python接口自動化之request請求封裝源碼分析_python
- 2022-09-03 Python實現(xiàn)求解最大公約數(shù)的五種方法總結(jié)_python
- 2022-05-15 Python獲取網(wǎng)絡(luò)圖片和視頻的示例代碼_python
- 2022-12-05 C語言實現(xiàn)順序表的基本操作的示例詳解_C 語言
- 2022-07-19 macOS Docker 內(nèi)存 CPU 占用過高,監(jiān)控到 com.Docker.hyperkit 進
- 2022-06-09 ASP.NET?Core中的Configuration配置一_基礎(chǔ)應(yīng)用
- 2023-07-29 koa2+sequelize中websocket的使用
- 2023-07-07 @Autowired 注解有什么用?@Qualifier 注解有什么用? @RequestMappi
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支