ASP.NET Core基于滑动窗口算法实现限流控制

前言

在实际项目中,为了保障服务器的稳定运行,需要对接口的可访问频次进行限流控制,避免因客户端频繁请求导致服务器压力过大。

而AspNetCoreRateLimit[1]是目前ASP.NET Core下最常用的限流解决方案。

查看它的实现代码,我发现它使用的固定窗口算法。

var entry = await _counterStore.GetAsync(counterId, cancellationToken);if (entry.HasValue)
{// entry has not expiredif (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow){// increment request countvar totalCount = entry.Value.Count + _config.RateIncrementer?.Invoke() ?? 1;// deep copycounter = new RateLimitCounter{Timestamp = entry.Value.Timestamp,Count = totalCount};}
}

固定窗口算法

固定窗口算法是将时间线划分为固定大小的窗口,并为每个窗口分配一个计数器。每个请求,根据其到达时间,被映射到一个窗口。如果窗口中的计数器已达到限制,则拒绝落在此窗口中的请求。

例如,如果我们将窗口大小设置为1分钟,每分钟允许10个请求:

0d872cbe024d573c7ea9bbd0a619d8a5.png

59秒的请求将被阻止,因为这时已经接受了10个请求。1分钟时计数器归零,所以1分01秒的请求可以接受。

固定窗口算法的问题主要在于,如果在窗口边缘发生大量请求,会导致限流策略失效。

比如,在59秒接收了9个请求,在1分01秒又可以再接收10个请求,相当于每分钟允许了20个请求。

滑动窗口算法

滑动窗口类似于固定窗口算法,但它通过将前一个窗口中的加权计数添加到当前窗口中的计数来计算估计数,如果估计数超过计数限制,则请求将被阻止。

具体公式如下:

估计数 = 前一窗口计数 * (1 - 当前窗口经过时间 / 单位时间) + 当前窗口计数

例如,假设限制为每分钟10个:

a1da9c5354baeed9dbbe7acfd6e9af5c.png

窗口[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<RateLimitMiddleware>();

结论

使用滑动窗口算法,可以有效避免固定窗口算法存在的窗口边缘大量请求无法限制的问题。

想了解更多内容,请关注我的个人公众号”My IO“

参考资料

[1]

AspNetCoreRateLimit: https://github.com/stefanprodan/AspNetCoreRateLimit

e4eb6e78bbd03381b1e887116beeb176.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 资源文件获取啥退,重拾Android之路之获得各种资源文件的方法

引言通常我们会在项目中频繁获取color、raw、drawable、mipmap、string等资源文件。因此&#xff0c;今天整理下获取资源文件的工具类方法。最新通用方法ContextCompat.getColor(this,R.color.activity_bg);ContextCompat.getDrawable(this,R.drawable.leak_canary_icon);最近在…

linux操作系统cp命令

转载于:https://www.cnblogs.com/skl374199080/p/3863918.html

WPF将数据库和GridView绑定并更改GridView模板

首先来看一下如何使用GridView,在前台的话代码如下&#xff1a;这里仅仅举出一个例子&#xff0c;GridView是作为子项嵌套在ListView中的&#xff0c;这里的数据源是通过绑定的方式来绑定到GridView中的。 <ListView Margin"15,115,15,48" Name"lstProducts&…

sql必读的九本书

2019独角兽企业重金招聘Python工程师标准>>> 原文地址 直接上书(书籍以后会陆续加上去)书籍下载地址 《MySQL必知必会》《SQL学习指南&#xff08;第2版 修订版&#xff09;》《MySQL技术内幕——InnoDB存储引擎》《Redis设计与实现》《ZooKeeper&#xff1a;分布式…

C语言之加入头文件<stdbool.h>可以使用true和false

1、头文件<stdbool.h>介绍 &#xff08;1&#xff09;使用了<stdbool.h>后&#xff0c;可使用true和false来表示真假。 &#xff08;2&#xff09;在循环语句中进行变量声明是C99中才有的&#xff0c;因此编译时显式指明 gcc -stdc99 prime.c 2、最简单的例子 3、…

Nginx负载均衡+转发策略

负载均衡负载均衡(详解)https://cloud.tencent.com/developer/article/1526664--示例1upstream www_server_pool { server 10.0.0.5; server 10.0.0.6&#xff1a;80 weight1 max_fails1 fails_timeout10s; server 10.0.0.7&#xff1a;80 weight1 max_fails2 fails_timeo…

ftp 断点续传 Android,Android使用FTP实现断点续传

Android使用FTP实现断点续传断点续传指的是在下载或上传时&#xff0c;将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分&#xff0c;每一个部分采用一个线程进行上传或下载&#xff0c;如果碰到网络故障&#xff0c;可以从已经上传或下载的部分开始继续上传下载未…

教育行业的互联网焦虑症

2019独角兽企业重金招聘Python工程师标准>>> 文/阑夕 2007年&#xff0c;前新东方名师刘一男在新东方在线&#xff08;网校&#xff09;上的全年课程收入是三千元&#xff0c;四年之后的2011年&#xff0c;这个数字飙升到了四十万&#xff0c;已经和刘一男当年实体…

常见的几种RuntimeException

一般面试中java Exception&#xff08;runtimeException &#xff09;是必会被问到的问题常见的异常列出四五种&#xff0c;是基本要求。更多的。。。。需要注意积累了常见的几种如下&#xff1a;NullPointerException - 空指针引用异常ClassCastException - 类型强制转换异常。…

Android之Error: ‘L‘ is not a valid file-based resource name character解决办法

1、问题 Error:Execution failed for task :mergeBYODReleaseResources.> /home/chenyu/Android_dev/sangfor/yangzhoushizhenghu/20161229_M7.3_R3/EMM/Source/Android/aWork/res/drawable-mdpi/LOGO.png: Error: L is not a valid file-based resource name character: Fi…

零基础学人工智能:TensorFlow 入门例子

识别手写图片 因为这个例子是 TensorFlow 官方的例子&#xff0c;不会说的太详细&#xff0c;会加入了一点个人的理解&#xff0c;因为TensorFlow提供了各种工具和库&#xff0c;帮助开发人员构建和训练基于神经网络的模型。TensorFlow 中最重要的概念是张量&#xff08;Tenso…

TextView 多文字字体颜色及多事件监听

像微信朋友圈点赞功能如&#xff1a;张三、李四、王五、这种格式 最早做法是在layout中创建一个父类容器如linearlayout然后在创建一个子layout&#xff0c;在代码中 通过for循环addView添加到父类容器当中&#xff0c;虽然可以实现效果但是这样无形中会增加很多view控件。 通过…

CA周记 - 用 Visual Studio Code 做基于 .NET MAUI 跨平台移动应用开发

自2010年以来&#xff0c;移动应用开发是非常热门的一个方向&#xff0c;从技术上我们经历了原生应用开发、基于 H5 的 Web App、混合模式的移动应用开发&#xff0c;再到跨平台移动应用开发。.NET 不仅是一个跨平台的应用&#xff0c;也是一个跨应用场景的平台。.NET的移动应用…

P2P网络穿越 NAT穿越

http://blog.csdn.net/mazidao2008/article/details/4933730 —————————————————————————————————————————————————————————————— 穿越NAT的意义&#xff1a; NAT是为了节省IP地址而设计的&#xff0c;但它隐藏了…

Eclipse之如何快速查看抽象函数实现

1、ctrl (open implementation) 2、ctrl T

页面获取Spring Security登录用户

1.在session中取得spring security的登录用户名如下&#xff1a;${session.SPRING_SECURITY_CONTEXT.authentication.principal.username} spring security 把SPRING_SECURITY_CONTEXT 放入了session 没有直接把username 放进去。下面一段代码主要描述的是session中的存的变量&…

android 骨骼动画库,cocos creator spine骨骼动画组件使用

1: 掌握sp.Skeleton组件的使用;spine骨骼动画工具1: 骨骼动画: 把动画打散, 通过工具&#xff0c;调骨骼的运动等来形成动画2: spine是一个非常流行的2D骨骼动画制作工具3: spine 动画美术人员导出3个文件:(1) .png文件:动画的”骨骼”的图片集;(2).atlas文件: 每个骨骼在图片集…

border:none 与border:0的区别

border:none与border:0的区别体现为两点&#xff1a;一是理论上的性能差异&#xff0c;二是浏览器兼容性的差异。 性能差异&#xff1a; 【border:0;】把border设为“0”像素效果等于border-width:0&#xff0c;浏览器依然对border-width、border-color进行了渲染&#xff0c;即…

C#导入导出.CSV文件

欢迎您成为我的读者&#xff0c;希望这篇文章能给你一些帮助。前言大家好&#xff0c;我是阿辉。今天和大家一起来看看&#xff0c;C#在处理流文件时,我们最常用的导出Excel文件是如何操作的。在日常的业务编码过程中&#xff0c;很多时候需求就要求导出Office能打开的表格文件…

LeetCode之Sum of Left Leaves

1、题目 Find the sum of all left leaves in a given binary tree. Example: 3/ \9 20/ \15 7There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.Subscribe to see which companies asked this question. 2、代码实现 /*** De…