算法原理

固定窗口算法又称计数器算法,是一种简单的限流算法。在单位时间内设定一个阈值和一个计数值,每收到一个请求则计数值加一,如果计数值超过阈值则触发限流,如果达不到则请求正常处理,进入下一个单位时间后,计数值清零,重新累计。

如上图所示,时间单位是1秒,阈值是3。

  • 第1秒3个请求,不会触发限流;

  • 第2秒1个请求,不会触发限流;

  • 第3秒4个请求,这一秒的前3个请求正常处理,第4个请求触发限流,会被拒绝处理。

  • 后续第4秒、第5秒不会触发限流,所有请求正常处理。

算法实现

这里讲两种实现方法:进程内即内存固定窗口算法、基于Redis的固定窗口算法。

进程内即内存固定窗口算法

使用字典,Key是限流目标,Value包括计数值和过期时间。处理请求时,首先从请求中提取限流目标,然后根据限流目标去字典中查找,如果不存在,则添加一个字典项,计数值是1,过期时间是当前时间+限流单位时间;如果存在,则检查是否过期,如果过期,则计数值归1,过期时间是当前时间+限流单位时间,如果未过期,则仅计数值加1。这里需要注意多线程问题,读写数据时需要加锁。

在C#语言中可以使用MemoryCache,它的缓存项有一个过期时间,不需要自己回收过期的项目。

进程内计数的方法最适合单实例处理的程序限流,多实例处理的情况下可能每个实例收到的请求数不均匀,不能保证限流效果。

基于Redis的固定窗口算法

Redis作为KV存储,类似于字典,而且也自带过期时间。处理请求时,首先从请求中提取限流目标,然后根据限流目标去Redis中查找,如果不存在,则添加KV项,Value值是1,过期时间是当前时间+限流单位时间;如果存在,则Value值加1。

这些操作逻辑可以封装在一个Lua script中,因为Lua script在Redis中执行时也是原子操作,所以Redis的限流计数在分布式处理时天然就是准确的。

算法应用

这里以限流组件 FireflySoft.RateLimit 为例,实现ASP.NET Core中的固定窗口限流。

1、安装Nuget包

有多种安装方式,选择自己喜欢的就行了。

包管理器命令:

Install-Package FireflySoft.RateLimit.AspNetCore

或者.NET命令:

dotnet add package FireflySoft.RateLimit.AspNetCore

或者项目文件直接添加:

<ItemGroup>
<PackageReference Include="FireflySoft.RateLimit.AspNetCore" Version="2.*" />
</ItemGroup>

2、使用中间件

在Startup中使用中间件,演示代码如下(下边会有详细说明):

public void ConfigureServices(IServiceCollection services)
{
...
app.AddRateLimit(new InProcessFixedWindowAlgorithm(
new[] {
new FixedWindowRule()
{
ExtractTarget = context =>
{
// 提取限流目标
return (context as HttpContext).Request.Path.Value;
},
CheckRuleMatching = context =>
{
// 判断当前请求是否需要限流处理
return true;
},
Name="fixed window limit rule",
LimitNumber=30, // 限流阈值
StatWindow=TimeSpan.FromSeconds(1) // 限流单位时间
}
})
);
...
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseRateLimit();
...
}

如上需要先注册服务,然后使用中间件。

注册服务的时候需要提供限流算法和对应的规则:

  • 这里使用进程内固定窗口算法InProcessFixedWindowAlgorithm,还可以使用RedisFixedWindowAlgorithm,需要传入一个Redis连接。
  • 限流阈值是30,限流单位时间是1秒。
  • ExtractTarget用于提取限流目标,这里是每个不同的请求Path。如果有IO请求,这里还支持对应的异步方法ExtractTargetAsync。
  • CheckRuleMatching用于验证当前请求是否限流。如果有IO请求,这里还支持对应的异步方法CheckRuleMatchingAsync。
  • 默认被限流时会返回HttpStatusCode 429,可以在AddRateLimit时使用可选参数error自定义这个值,以及Http Header和Body中的内容。

基本的使用就是上边例子中的这些了。

如果还是基于传统的.NET Framework,则需要在Application_Start中注册一个消息处理器RateLimitHandler,算法和规则部分都是共用的,具体可以看Github上的使用说明:https://github.com/bosima/FireflySoft.RateLimit#aspnet


FireflySoft.RateLimit 是一个基于 .NET Standard 的限流类库,其内核简单轻巧,能够灵活应对各种需求的限流场景。

其主要特点包括:

  • 多种限流算法:内置固定窗口、滑动窗口、漏桶、令牌桶四种算法,还可自定义扩展。
  • 多种计数存储:目前支持内存、Redis两种存储方式。
  • 分布式友好:通过Redis存储支持分布式程序统一计数。
  • 限流目标灵活:可以从请求中提取各种数据用于设置限流目标。
  • 支持限流惩罚:可以在客户端触发限流后锁定一段时间不允许其访问。
  • 动态更改规则:支持程序运行时动态更改限流规则。
  • 自定义错误:可以自定义触发限流后的错误码和错误消息。
  • 普适性:原则上可以满足任何需要限流的场景。

Github开源地址:https://github.com/bosima/FireflySoft.RateLimit

收获更多架构知识,请关注公众号 萤火架构。原创内容,转载请注明出处。

ASP.NET Core中使用固定窗口限流的更多相关文章

  1. ASP.NET Core中使用滑动窗口限流

    滑动窗口算法用于应对请求在时间周期中分布不均匀的情况,能够更精确的应对流量变化,比较著名的应用场景就是TCP协议的流量控制,不过今天要说的是服务限流场景中的应用. 算法原理 这里假设业务需要每秒钟限流 ...

  2. ASP.NET Core中使用令牌桶限流

    在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...

  3. ASP.NET Core中使用漏桶算法限流

    漏桶算法是限流的四大主流算法之一,其应用场景各种资料中介绍的不多,一般都是说应用在网络流量控制中.这里举两个例子: 1.目前家庭上网都会限制一个固定的带宽,比如100M.200M等,一栋楼有很多的用户 ...

  4. ASP.NET Core中如何对不同类型的用户进行区别限流

    老板提出了一个新需求,从某某天起,免费用户每天只能查询100次,收费用户100W次. 这是一个限流问题,聪明的你也一定想到了如何去做:记录用户每一天的查询次数,然后根据当前用户的类型使用不同的数字做比 ...

  5. ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...

  6. [小技巧]ASP.NET Core中如何预压缩静态文件

    原文地址:Pre-compressed static files with ASP.NET Core 作者:Gunnar Peipman 译者:Lamond Lu 译文:https://www.cnb ...

  7. 为什么我的会话状态在ASP.NET Core中不工作了?

    原文:Why isn't my session state working in ASP.NET Core? Session state, GDPR, and non-essential cookie ...

  8. ASP.NET Core中使用GraphQL - 第一章 Hello World

    前言 你是否已经厌倦了REST风格的API? 让我们来聊一下GraphQL. GraphQL提供了一种声明式的方式从服务器拉取数据.你可以从GraphQL官网中了解到GraphQL的所有优点.在这一系 ...

  9. ASP.NET Core中使用GraphQL - 第二章 中间件

    前文:ASP.NET Core中使用GraphQL - 第一章 Hello World 中间件 如果你熟悉ASP.NET Core的中间件,你可能会注意到之前的博客中我们已经使用了一个中间件, app ...

随机推荐

  1. 修改 oracle 数据库的 sys 账号密码,ERROR at line 1: ORA-01034: ORACLE not available

    挺久没有登录的 oracle 数据库,因为公司要求加固密码,登录后修改失败 1.启动数据库的同时启动控制文件.数据文件,提示:cannot mount database in EXCLUSIVE mo ...

  2. Spring Security OAuth2 单点登录

    1. OAuth 2.0 OAuth(Open Authorization)为用户资源的授权提供了一个安全的.开放而又简易的标准.最简单的理解,我们可以看一下微信OAuth2.0授权登录流程: 通过O ...

  3. 【JavaScript】JS的坚实基础

    前言 ​ 考虑到在后面的开发中,需要大量的使用js语言去进行开发,所以准备重新规整一下javascript的知识点,专门开了一个js的专栏,用来复习一下js语言.万事开头难,要是后面写的有问题的,欢迎 ...

  4. python3+Robotframework+ride+Selenium2Library+Autoitlibrary环境搭建

    1.安装python3.8 第一步是安装Python:https://www.python.org/,RF框架是基于python 的,所以一定要有python环境.将python-3.8.2-amd6 ...

  5. inline-block布局VS浮动布局

        a.不同之处:对元素设置display:inline-block ,元素不会脱离文本流,而float就会使得元素脱离文本流,且还有父元素高度坍塌的效果     b.相同之处:能在某程度上达到一 ...

  6. 大爽Python入门教程 1-5 答案

    大爽Python入门公开课教案 点击查看教程总目录 1 方向变换 >>> 51//4 12 >>> 51%4 3 答: 向左转51次之后, 小明面朝东方, 转过了1 ...

  7. Java 多线程 - 总结概述

    概述 菜鸟教程: Java 给多线程编程提供了内置的支持. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 多线程是多任务的一种特别的形式,但多线程 ...

  8. .net工程师学习vue的心路历程(一)

    实习一年后,想做一个属于自己的博客网站,准备用core api去搭建服务端接口,前端准备采用vue这样的一个框架.本身时一个服务端程序员,所以来学习记录一些vue的知识点,有什么不足的希望大家指正,谢 ...

  9. 手把手教你汇编 Debug

    关于汇编的第一篇文章: 爱了爱了,这篇寄存器讲的有点意思 Hello大家好,我是程序员cxuan!我们上篇文章了解了一下基本的寄存器,这篇文章我们来进行实际操作一下. 原文链接:手把手教你汇编 Deb ...

  10. TCP、三次握手、四次挥手(图解)

    传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议,为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传 ...