ASP.NET Core中使用滑动窗口限流的问题及场景分析

目录
  • 算法原理
    • 漏检
    • 太刚
  • 算法实现
  • 进程内即内存滑动窗口算法
  • 基于Redis的滑动窗口算法
  • 应用算法
    • 1、安装Nuget包
    • 2、使用中间件

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

算法原理

这里假设业务需要每秒钟限流100次,先来看固定窗口算法的两个问题:

漏检

如下图所示,单看第1秒和第2秒,其请求次数都没有超过100,所以使用固定窗口算法时不会触发限流。但是第1秒的后500ms的请求数加上第2秒的前500毫秒的请求数就超过了100,这时候可能会给系统带来伤害,使用固定窗口算法时不能检测到这种情况。

太刚

针对漏检的问题,你可能会说,可以把时间窗口设置为500ms,把限流阈值设置为50。那么来看下图,除了第2个计数周期超过了50,从而触发限流,前后几个计数周期的请求都很正常,甚至都不会超过阈值的50%,可能第2个计数周期的情况实在太特殊,1天都不会出现第2次,如果对系统不会造成影响,能不能通融下,做不到!固定窗口算法这时候就会显得太过刚性。

那么滑动窗口如何来解决这两个问题呢?还是先来看图:

如上图所示:

  • 滑动窗口的时间跨度是1秒,每个小计数周期的时间跨度是500ms,此处的滑动窗口包含2个小计数周期。
  • 随着时间的前进,滑动窗口包含的小计数周期会以500ms为单位向前移动,但始终是包含2个小计数周期。
  • 判断是否限流时,需要将当前滑动窗口包含的2个小计数周期的计数值加起来。
  • 相比固定窗口计数器算法,滑动窗口可以有效减少漏检,如上图滑动窗口移动到了500-1500ms,发现总数超过100,则触发限流;滑动窗口在0-1000ms、1000-2000ms时都不会触发限流,即使其中某个小周期的计数值超过了阈值的半数,但是总数没有超过100,就不会限流,能够应对极少出现的突发流量情况。

从分析还可以看出,滑动窗口的小周期划分的越多,则检测越准确,但用于跟踪的计数也越多,使用的内存和计算量都会增大。

算法实现

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

进程内即内存滑动窗口算法

这里介绍一种性能比较高的方法,使用数组实现滑动窗口,这是环形队列的一种特例,如下图所示:

  • 假设滑动窗口需要5个小的计数周期,则初始化一个长度为5的整形数组,数字表示数组中的第几个元素。
  • 我们知道队列有头有尾,从队头取出数据,向队尾插入数据,带括号的数字表示是队列中的第几个元素。
  • 滑动窗口向前移动时,队尾向右移动1位,同时队头也向右移动1位。
  • 队尾和队头向右移动都可能会溢出数组,此时让它们回到数组的起始位置,即图中数组的第1个位置。

关于这个算法的详细介绍,可以看这篇文章:如何使用数组实现滑动窗口

基于Redis的滑动窗口算法

基于Redis时也可以使用类似环形队列的方法,比如定义5个KV作为数组的5个元素。不过我之前实现时采用了一种更直观的方式,每个小的计数周期都创建一个KV,同时设置一个绝对超过滑动窗口时间跨度的过期时间,用不到的小计数周期不会一直占用内存;判断是否触发限流时,把这些小滑动窗口的计数值累加起来就可以了。当然实际实现时还需要完善一些细节上的处理,比如怎么找到这些小计数周期,会有多种方案,存起来或者临时计算都可以。

这些操作逻辑可以封装在一个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 InProcessSlidingWindowAlgorithm(
                new[] {
                		// 构造函数有两个参数:滑动窗口的时间长度、小计数周期的时间长度
                    new SlidingWindowRule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1))
                    {
                        ExtractTarget = context =>
                        {
                        		// 提取限流目标
                            return (context as HttpContext).Request.Path.Value;
                        },
                        CheckRuleMatching = context =>
                        {
                        		// 判断当前请求是否需要限流处理
                            return true;
                        },
                        Name="sliding window limit rule",
                        LimitNumber=100, // 限流阈值,这里即5秒最多100次请求
                    }
                })
            );
            ...
        }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...
            app.UseRateLimit();
            ...
        }

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

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

  • 这里使用进程内固定窗口算法InProcessSlidingWindowAlgorithm,还可以使用RedisSlidingWindowAlgorithm,需要传入一个Redis连接。两种算法都支持同步和异步方法。
  • 限流阈值是100,限流滑动窗口是5秒,小计数周期是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

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

其主要特点包括:

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

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

到此这篇关于ASP.NET Core中使用滑动窗口限流的文章就介绍到这了,更多相关ASP.NET Core限流内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Asp.NET Core 限流控制(AspNetCoreRateLimit)的实现

    起因: 近期项目中,提供了一些调用频率较高的api接口,需要保障服务器的稳定运行:需要对提供的接口进行限流控制.避免因客户端频繁的请求导致服务器的压力. 一.AspNetCoreRateLimit 介绍 AspNetCoreRateLimit 是一个ASP.NET Core速率限制的解决方案,旨在控制客户端根据IP地址或客户端ID向Web API或MVC应用发出的请求的速率.AspNetCoreRateLimit包含一个 IpRateLimitMiddleware 和 ClientRateLim

  • ASP.NET Core对不同类型的用户进行区别限流详解

    前言 老板提出了一个新需求,从某某天起,免费用户每天只能查询100次,收费用户100W次. 这是一个限流问题,聪明的你也一定想到了如何去做:记录用户每一天的查询次数,然后根据当前用户的类型使用不同的数字做比较,超过指定的数字就返回错误. 嗯,原理就是这么简单.不过真正写起来还要考虑更多问题: 统计数据的数据结构是什么样的?字典 or 行记录? 统计数据记录到哪里?内存 or MySQL or Redis? 分布式应用怎么精确计数?分布式锁 or 队列 or 事务? 吞吐量比较大时如何扛得住?内存

  • ASP.NET Core使用固定窗口限流

    目录 算法原理 算法实现 进程内即内存固定窗口算法 基于Redis的固定窗口算法 算法应用 1.安装Nuget包 2.使用中间件 算法原理 固定窗口算法又称计数器算法,是一种简单的限流算法.在单位时间内设定一个阈值和一个计数值,每收到一个请求则计数值加一,如果计数值超过阈值则触发限流,如果达不到则请求正常处理,进入下一个单位时间后,计数值清零,重新累计. 如上图所示,时间单位是1秒,阈值是3. 第1秒3个请求,不会触发限流: 第2秒1个请求,不会触发限流: 第3秒4个请求,这一秒的前3个请求正常

  • ASP.NET Core中使用令牌桶限流的实现

    在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某一秒钟会达到120次请求,接着很快又会恢复正常,假设这种突发的流量不会对系统稳定性带来实质性的影响,则可以在一定程度上允许这种瞬时的突发流量,从而为用户带来更好的可用性体验.这就是令牌桶算法的用武之地. 该算法的基本原理是:有一个令牌桶,容量是X,每Y单位时间会向桶中放入Z个令牌,如果桶中的令牌数超

  • ASP.NET Core中使用滑动窗口限流的问题及场景分析

    目录 算法原理 漏检 太刚 算法实现 进程内即内存滑动窗口算法 基于Redis的滑动窗口算法 应用算法 1.安装Nuget包 2.使用中间件 滑动窗口算法用于应对请求在时间周期中分布不均匀的情况,能够更精确的应对流量变化,比较著名的应用场景就是TCP协议的流量控制,不过今天要说的是服务限流场景中的应用. 算法原理 这里假设业务需要每秒钟限流100次,先来看固定窗口算法的两个问题: 漏检 如下图所示,单看第1秒和第2秒,其请求次数都没有超过100,所以使用固定窗口算法时不会触发限流.但是第1秒的后

  • redis zset实现滑动窗口限流的代码

    目录 限流 rediszset特性 滑动窗口算法 java代码实现 补充:RediszSet实现滑动窗口对短信进行防刷限流 前言 示例代码 限流 需求背景:同一用户1分钟内登录失败次数超过3次,页面添加验证码登录验证,也即是限流的思想. 常见的限流算法:固定窗口计数器:滑动窗口计数器:漏桶:令牌桶.本篇选择的滑动窗口计数器 redis zset特性 Redis 有序集合(sorted set)和集合(set)一样也是 string 类型元素的集合,且不允许重复的成员.不同的是每个元素都会关联一个

  • go redis实现滑动窗口限流的方式(redis版)

    之前给大家介绍过单机当前进程的滑动窗口限流 , 这一个是使用go redis list结构实现的滑动窗口限流 , 原理都一样 , 但是支持分布式 原理可以参考之前的文章介绍 func LimitFreqs(queueName string, count uint, timeWindow int64) bool { currTime := time.Now().Unix() length := uint(ListLen(queueName)) if length < count { ListPus

  • 解决ASP.NET Core中使用漏桶算法限流的问题

    目录 算法原理 算法实现 进程内即内存漏桶算法 基于Redis的漏桶算法 应用算法 1.安装Nuget包 2.使用中间件 漏桶算法是限流的四大主流算法之一,其应用场景各种资料中介绍的不多,一般都是说应用在网络流量控制中.这里举两个例子: 1.目前家庭上网都会限制一个固定的带宽,比如100M.200M等,一栋楼有很多的用户,那么运营商怎么保证某些用户没有使用过多的带宽,从而影响到别人呢?这时就可以使用漏桶算法,限制每个用户访问网络的最大带宽,当然实际会比这复杂很多. 2.有一个祖传接口,当时写的时

  • Java 实现滑动时间窗口限流算法的代码

    在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码: package cn.dijia478.util; import java.time.LocalTime; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; /** * 滑动时间窗

  • Java中4种经典限流算法讲解

    目录 限流是什么? 常见的限流算法 固定窗口限流算法 滑动窗口限流算法 漏桶算法 令牌桶算法 最近,我们的业务系统引入了Guava的RateLimiter限流组件,它是基于令牌桶算法实现的,而令牌桶是非常经典的限流算法.本文将跟大家一起学习几种经典的限流算法. 限流是什么? 维基百科的概念如下: In computer networks, rate limiting is used to control the rate of requests sent or received by a net

  • ASP.NET Core中预压缩静态文件的方法步骤

    前言 Web应用程序的优化是非常重要,因为使用更少的CPU,占用更少的带宽可以减少项目的费用. 在ASP.NET Core中我们可以很容易的启用响应压缩,但是针对预压缩文件,就需要做一些额外的功能了. 这篇博客文章展示了如何在ASP.NET Core中预压缩静态文件. 下面话不多说了,来一起看看详细的介绍吧 为什么需要预压缩文件? 虽然在从服务器请求文件时, 我们可以动态压缩文件,但这意味这Web服务器需要做更多的额外工作. 其实只有在新的应用程序部署时才会更改要压缩的文件. 越好的压缩效果需要

  • 详解ASP.NET Core中配置监听URLs的五种方式

    默认情况下,ASP. NET Core应用会监听一下2个Url: http://localhost:5000 https://localhost:5001 在本篇博文中,我将展示如何使用五种不同的方式改变应用监听的URLs. 在ASP.NET Core项目启动时,有多种配置监听Url的方式,在我之前的一篇博客中,已经展示了在ASP.NET Core 1.0中如何应用不同的方式配置,在ASP.NET Core 3.x中,大部分方式还是一样的. UseUrls() - 在Program.cs配置程序

随机推荐