ASP.NET Core-限流(Rate Limiting)

发布时间 2023-12-21 16:41:10作者: 木乃伊人

一、应用场景

       微服务架构中,限流功能一般由网关提供。而对于很多非微服务化的系统,可能并没有网关[无论是因为成本还是复杂度],在这种场景下,为了实现限流,.NET 7中提供了限流中间件 Rate Liniting。

二、实现

       首先,SDK版本 >= 7。

       然后添加代码注册。

       微软为我们提供了4中常用的限流算法:

  1. 固定窗口限流器【FixedWindowLimiter
  2. 滑动窗口限流器【SlidingWindowLimiter
  3. 令牌桶限流器【TokenBucketLimiter
  4. 并发限流器【ConcurrencyLimiter

      通常我们会注册一个命名限流策略,并在该策略内指定限流算法,以及其他限流逻辑。需要注意的是,UseRateLimiter的位置,若限流行为作用于特定路由,这限流中间件必须放到UseRouting之后。

三、详情

       3.1、固定窗口限流器

                固定窗口限流器比较简单,限流方式如下:

                【原理】:使用固定的时间长度来限制请求数量。如固定窗口长度为10s,则每10s就会切换(销毁并创建)一个新窗口,在每个单独的窗口内,限制请求数量。

                 【特点】:

                               优点:实现简单,内存占用低。

                               缺点:

                                         1、当窗口QPS到达阈值,流量会被瞬间切断,不能平滑处理突发流量(实际应用中理想效果是让流量平滑的进入系统)

                                         2、窗口切换时可能出现2倍QPS。如窗口大小为1s,阈值为100,窗口1在后500ms内处理100个请求,窗口2在前500ms内也处理100个请求,这样就导致实际在1s内处理了200个请求。 

 

          

#region 注册限流中间件

builder.Services.AddRateLimiter(options =>
{
    //1、固定窗口限流策略
    //配置说明:该固定窗口60s时间内,可以最多有100+50=150个请求,100个会被处理,50个会被排队,其他则会在一定时间后拒绝返回 RejectionStatusCode
    options.AddFixedWindowLimiter(policyName:"fixed",fixedOptions =>
    {
        fixedOptions.PermitLimit = 100;//每个窗口时间范围内,允许100个请求被处理
        fixedOptions.Window = TimeSpan.FromSeconds(60); //窗口大小。即窗口时间长度60s。必须>TimeSpan.Zero
        fixedOptions.QueueLimit = 50;//窗口阈值。即每个窗口时间范围内,最多允许的请求个数。该值必须>0。当窗口请求达到最大值,后续请求会进入排队。该值用于设置对垒大小(即允许几个请求在排队队列等待)
        fixedOptions.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;//排队请求的处理顺序。这里设置为有限处理先来的请求
        fixedOptions.AutoReplenishment = true;//开启新窗口时是否自动重置请求限制,默认true。如果是false,则需要手动调佣 FixedWindowRateLimiter.TryReplenish来重置
    });
});

#endregion

//使用限流器
app.UseRateLimiter();
View Code

       3.2、滑动窗口限流器

               滑动窗口限流器是固定窗口限流器的升级版。

                【原理】:在固定窗口限流器的基础上,它将每个窗口划分为多个段,每经过一个段的时间间隔(=窗口时间/窗口段的个数),窗口就会向后滑动一段,所以称为滑动窗口(窗口大小仍是固定的)。当窗口滑动后,会“吃进”一个段(称为当前段),并“吐出”一个段(称为过期端),过期段会被回收,回收的请求数可以用于当前段。

                【特点】:

                                优点:按段滑动处理,相对于固定窗口,可以对流量进行更精准的控制,更平滑的处理突发流量,并且段划分的越多,移动更平滑。

                                缺点:对时间精度要求高,比固定窗口实现复杂,占用内存更高。

 

//2、滑动窗口限流则略
//配置说明:窗口时间长度为30s,每个窗口内,最多允许100个请求,窗口段数3,每个段的时间间隔为30/3=10s,即窗口每10s滑动一段。
options.AddSlidingWindowLimiter(policyName:"sliding", slidingOptions =>
{
    slidingOptions.PermitLimit = 100;
    slidingOptions.Window = TimeSpan.FromSeconds(30);
    slidingOptions.QueueLimit = 2;
    slidingOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    slidingOptions.AutoReplenishment = true;//开启新窗口时是否自动重置请求限制,默认true
    slidingOptions.SegmentsPerWindow = 3;
});
View Code

 

                【理解】:滑动窗口就是为了解决固定窗口2n的问题。

                                  计算公式:估计数 = 前一窗口技术 * (1 - 当前窗口经过时间/单位时间)+ 当前窗口计数

                                  举个例子:窗口为每分钟最大处理10个请求。这实际计算后数据为:估计数=9 * (1-25%) + 5 = 11.75 > 10,则最后的一次请求会被阻止。虽然在单个窗口内,请求数都没有超过最大限制,但是超过了加权计算结果。                               

                              另外,滑动窗口的段,可以理解为每次加权计算移动的时间距离。如果最近的2次请求相距两个时间窗口,则可以认为前一窗口计数为零,重新开始计数。

 

       3.3、令牌桶限流器

                 令牌桶限流器是一种限制数据平均传输速率的限流算法。

                 【原理】:想象有一个桶,每个固定时间段会向桶内放入固定数量的令牌(token),当桶内令牌装满时,新的令牌将会被丢弃。当请求流量进入时,会先从桶内拿 1 个令牌,拿到了则该请求会被处理,没拿到则会在队列中等待,若队列已满,则会被限流拒绝处理。

                  【特点】:可以限制数据的平均传输速率,还可以一次性耗尽令牌应对突发流量,并平滑地处理后续流量,是一种通用的算法。

 

             

//策略说明:桶最多装4个令牌,每10秒发放一次令牌,每次发放2个令牌,所以在一个发放周期,最多可以处理4个请求,至少可以处理2个请求。
options.AddTokenBucketLimiter(policyName:"token_bucket", tokenBucketOptions =>
{
    tokenBucketOptions.TokenLimit = 4;//桶最多可以装的令牌数,发放的多余令牌会被丢弃
    tokenBucketOptions.ReplenishmentPeriod = TimeSpan.FromSeconds(10);//令牌发放周期
    tokenBucketOptions.TokensPerPeriod = 2;//每个周期发放令牌数
    tokenBucketOptions.QueueLimit = 2;//当桶内的令牌全部被拿完(token=0)时,后续请求会进入排队
    tokenBucketOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    tokenBucketOptions.AutoReplenishment = true;//进入新令牌发放周期,是否自动发放令牌。如果设置为false,则需要手动调用 TokenBucketRateLimiter.TryReplenish来发放
});
View Code

 

       3.4、并发限流器

               并发限流器不是限制一段时间内的最大请求数,而是限制并发数。

               【原理】:限制同一时刻并发请求的数量。

               【特点】:可以充分利用服务器的性能,当出现突发流量时,服务器负载可能会持续过高。

 

//策略说明:最大并发请求4,超过最大并发请求,则后续最多2个请求进入排队队列。
options.AddConcurrencyLimiter(policyName:"concurrency", concurrencyOptions =>
{
    concurrencyOptions.PermitLimit = 4;//最大并发请求数
    concurrencyOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    concurrencyOptions.QueueLimit = 2;//当并发请求数达到最大,后续请求进入排队,该参数用于配置队列大小
});
View Code