波斯马BOSSMA Information Technology

FireflySoft.RateLimit使用与原理

发布时间:2021年1月24日 / 分类:DOTNET / 4,439 次浏览 / 评论

限流一般是为了解决因拥挤导致服务无法正常提供的问题。比如常见的地铁限流,如果很多人在很短的时间内快速涌入,超出站台内的空间或列车的可运载人数,就会容易引发安全事故。

在github上使用C#编写的限流程序有很多,翻看了一些,大部分都有很多的场景限制,比如只能用在asp.net core,或者只能使用IP或者ClientId限制,或者对分布式部署不友好,又或者支持的限流算法比较单一。

FireflySoft.RateLimit的目的是提供一个更为基础的限流组件,可以用于各种限流业务场景,可以用于多种形式的程序,可以容纳多种限流算法,支持分布式统一限流,支持灵活的限流目标控制,提供方便的自定义机制,包括各种配置、算法和数据持久化等,同时使用起来更为简便。

下面先简单介绍其使用方法,然后再看一下其逻辑原理。

使用方法

以一个简单的ASP.NET Core WebAPI固定窗口限流为例,要求每个接口每秒钟限制50次请求。使用FireflySoft.RateLimit只需两步:安装Nuget包、使用限流中间件。

1、安装Nuget包

通过包管理器安装:

Install-Package FireflySoft.RateLimit.AspNetCore -Version 1.0.0

或者通过.NET CLI:

dotnet add package FireflySoft.RateLimit.AspNetCore --version 1.0.0

当然你也可以自己编译代码,添加本地引用,开源项目地址:https://github.com/bosima/FireflySoft.RateLimit

2、使用限流中间件

打开 Startup.cs,在Configure方法中添加 app.UseRateLimit,并提供限流的相关设置,包括:限流的请求类型、限流使用的算法、限流的规则、限流计数的持久化方式、限流时的错误消息等等。示例代码如下:

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

    app.UseRateLimit(new Core.RateLimitProcessor<HttpContext>.Builder()
        // .WithError(new Core.RateLimitError()
        // {
        //     Code=429,
        //     Message = "The system is busy, please try again later"
        // })
        // .WithStorage(new RedisStorage(StackExchange.Redis.ConnectionMultiplexer.Connect("localhost")))
        .WithAlgorithm(new FixedWindowAlgorithm<HttpContext>( new[] {
            new FixedWindowRateLimitRule<HttpContext>()
            {
                ExtractTarget = context =>
                {
                    // for all path, you can customize it
                    return "rule1-" + context.Request.Path.Value;
                },
                CheckRuleMatching = context =>
                {
                    // limit every request, you can customize it
                    return true;
                },
                Name="general limit rule",
                LimitNumber=30,
                LockSeconds=1,
                StatWindow=TimeSpan.FromSeconds(1)
            }
        }))
        .Build());

    ...
}

复制上边的代码到你的项目中,打开Postman,发起一个Runner,马上就可以体验到FireflySoft.RateLimit的限流效果。

FireflySoft.RateLimit还提供了另外两个Nuget包:

  • FireflySoft.RateLimit.AspNet用于基于.NET Framework的ASP.NET项目限流,.NET版本必须是4.6.1及以上。
  • FireflySoft.RateLimit.Core是FireflySoft.RateLimit.的核心类库,可以集成到任何类型的项目中进行限流处理。不仅仅是Web服务限流,各种需要数量限制的业务处理都可以使用。

逻辑原理

1、限流处理器

使用FireflySoft.RateLimit首先需要创建一个限流处理器RateLimitProcessor的实例,然后用它来过滤每一个请求。

创建RateLimitProcessor的实例,至少需要提供两个信息:被限流的请求类型、限流算法和规则。其它还有限流计数持久化方式、限流错误信息,如果不提供会使用默认值。这里先不展开,后边会详细介绍它们。

创建RateLimitProcessor需要采用构造器模式,如果你使用ASP.NET Core应该很熟悉,如果没什么印象打开Program.cs就可以看到了。

为了提高效率,这个限流处理器一般会统一放在某个过滤器中,这个过滤器在ASP.NET中可以是一个消息处理器,在ASP.NET Core中一般就是一个中间件。为了方便集成,FireflySoft.RateLimit已经提供了一个基于ASP.NET的消息处理器和一个基于ASP.NET Core的中间件,通过Github和Nuget都可以很方便的找到他们,Github上还提供了演示程序。

2、被限流请求类型

被限流请求类型就是需要被限制的业务请求的类型,在ASP.NET中他可能是HttpRequestMessage,在ASP.NET Core中它可能是HttpContext,在其它业务中它可能是你自定义的一个类型XXXRequest。

被限流请求类型很重要,因为不同的业务可能使用不同的请求类型,而限流处理程序需要从当前请求中提取或关联到限流目标,明确请求类型才能确定提取方法,否则对请求进行限制将无所适从。

3、限流算法和规则

FireflySoft.RateLimit提供了四种常见的限流算法:固定窗口、滑动窗口、漏桶、令牌桶。固定窗口相对最简单,无论是算法的时间复杂度还是分布式环境实现难度都比较有优势,如果需求是在较短的时间内进行限制,比如每秒限制多少次,使用这种算法是最合适的。但是实际场景中请求在时间分布中可能不太均匀,时多时少,根据需求的不同,可能需要选择其它三种限流算法,这里不做说明了,网上已经有很多的场景选择说明。

初始化限流算法还需要提供对应的限流规则,因为不同的算法往往需要不同的参数,这里很难对不同的算法提供完全统一的限流规则,不过这些规则确实是有共同设置项的,比如限流锁定时间、目标提取方法、是否应用限流处理的判断方法等。

以上文【使用方法】中的限流代码为例,做一些介绍:

  • FixedWindowAlgorithm是此限流组件提供的固定窗口限流算法。
  • FixedWindowRateLimitRule是此限流组件提供的固定窗口限流规则类型;
  • HttpContext是业务中需要限制的请求类型,这里是Http请求上下文类型;
  • StatWindow是固定窗口的大小,是一个时间跨度;
  • LimitNumber是限流值,在StatWindow时间内请求数超过它就会触发限流;
  • LockSeconds是触发限流后的锁定时间,此时间内请求都会被阻止,不需要锁定时可以不设置;
  • ExtractTarget传递一个方法用于从请求中提取限流目标,这里是用户的每一个请求路径;
  • CheckRuleMatching传递一个方法用于检查当前请求是否需要限流检查,这里return true代表所有请求都要经过限流检查。

这里有两个比较有意思的设置:ExtractTarget和CheckRuleMatching,他们共同作用,让用户可以完全自由的定制自己限流的目标和条件,不固定是IP、ClientId或者Url。其它算法规则中每个设置项的含义可以通过其注释了解到。

如果这几个算法还不能满足要求,可以通过实现IRateLimitAlgorithm来定义一个新的算法。

4、计数持久化方式

FireflySoft.RateLimit中的限流计数目前支持保存在内存或者Redis中,也可以通过实现IRateLimitStorage来定义一个新的存储器。

对于只需要部署一份的程序,绝大部分情况下使用内存就够了;但是如果限流的时间窗口比较长,比如1小时限制300次,重启就会丢失计数,这可能是个风险,此时使用Redis会比较合适。

对于需要部署多份的程序,如果请求是基本均匀的,并且在每个部署之间的分配也是均匀的,那么使用内存存储器也未尝不可,将总的限流数平均分配在每一个部署中,只要有一个部署触发限流,其它部署很大几率上也会触发限流。但是对于需要部署多份的情况,采用更持久的Redis方式才是稳妥的。

相比内存访问,Redis访问需要网络交互,这会造成一定的性能损失,访问量很大时也会产生拥堵,不过也可以将请求分散到多个Redis的方式进行缓解;同时分布式环境下数据一致性的实现难度更大,即使使用Redis,比如限流处理中必然会涉及的各种时间,不同节点之间的时间不可能绝对一致,越短的时间窗口协调难度越大。因此使用滑动窗口、漏桶、令牌桶等分布式实现难度较大的算法时,需要注意时间单位的设置。

5、限流错误信息

限流错误信息是触发限流时限流检查结果中附带的信息,目前每个限流处理器允许设置一个统一的RateLimitError,方便业务侧进行统一处理。默认限流错误Code是429,Message为null。你也可以不使用这个定义的值,根据当前限流目标和限流规则构造自己的错误信息。

6、限流锁定

限流锁定是触发限流时的惩罚性处理。FireflySoft.RateLimit通过设置限流规则中的LockSeconds,定义触发限流后的锁定时间,此时间内的请求都会被认定触发限流规则,而不被允许通过;如果不需要锁定忽略这个设置就可以了。

了解源码

FireflySoft.RateLimit已经在Github开源:https://github.com/bosima/FireflySoft.RateLimit

项目说明
FireflySoft.RateLmit.CoreFireflySoft.RateLmit的算法、规则、持久化和其它核心代码。
FireflySoft.RateLmit.Core.Sample使用FireflySoft.RateLmit.Core的示例程序。
FireflySoft.RateLimit.AspNet为基于 .NET Framework的ASP.NET提供的限流组件。
FireflySoft.RateLimit.AspNet.Sample使用FireflySoft.RateLimit.AspNet的示例程序。
FireflySoft.RateLimit.AspNetCoreASP.NET Core限流中间件。
FireflySoft.RateLimit.AspNetCore.Sample使用FireflySoft.RateLimit.AspNetCore的示例程序。
本博客所有文章如无特别注明均为原创。
复制或转载请以超链接形式注明转自波斯马,原文地址《FireflySoft.RateLimit使用与原理

关键字:

建议订阅本站,及时阅读最新文章!
【上一篇】 【下一篇】

发表评论