自FireflySoft.RateLimit公布以来,帮忙了不少须要在.net中进行限流解决的用户。前段时间有个开发者发了一个pull request,粗心是Redis重启的时候Lua script会失落,然而程序中还认为它存在,所以就会始终抛出异样,那位同学通过捕获一个特定异样再reload Lua script的形式解决了这个问题。通过一段时间的测,试运行良好,因为这个问题还是绝对常见的,所以就公布了一个版本 2.0.2,倡议通过nuget尽快降级。

前段时间还有用户问怎么在程序执行过程中动静更改限流的阈值,比方原来限流100/s,当初服务性能更好了,要改成限流300/s。FireflySoft.RateLimit底层是反对的,通过IAlgorithm.UpdateRules或者UpdateRulesAsync即可实现。不过这只是一个更新API,理论还须要开发者本人去做更多的工作,比方定义限流阈值的数据格式、从其它配置零碎中定时获取最新的限流阈值等。为了更不便开发者应用这个类库,同时恰逢.NET 6正式公布,所以这里用.NET6编写一个Demo程序,能够实现程序运行时动静更新限流阈值。

限流需要

这里假如需要是这样的:

  • 有一个天气服务,蕴含两个接口:GetToday(获取明天的天气)、GetTomorrow(获取今天的天气)。
  • 对每个访问者别离独自限流,具体限流阈值:GetToday 20次/秒、GetTomorrow 10次/秒,所有接口总计 25次/秒。
  • 每秒的拜访次数并不平均,有肯定的突发申请。大部分状况下低于限流阈值,极少数时可能会超出限流阈值30%。

限流配置

FireflySoft.RateLimit中不同的限流算法有不同的限流规定定义,因为有突发状况,所以这里采纳令牌桶算法。依据限流需要,这里定义了一个限流配置,它是利用到每一个用户的。

public class RateLimitConfiguration{    public string? Path { get; set; }    public LimitPathType PathType { get; set; }    public int TokenCapacity { get; set; }    public int TokenSpeed { get; set; }}

其中:

  • Path 用来定义接口门路,形如:/WeatherForecast/GetToday
  • PathType 指定利用到的接口类型:单个接口还是所有接口
  • TokenCapacity 是令牌桶容量
  • TokenSpeed 是令牌放入速度,这里固定单位是:个/秒,FireflySoft.RateLimit反对更小的工夫单位。

同时为了不便限流规定的更新,它能够用来传输或者长久化到各种存储中。我把配置保留在MySQL中,更改限流阈值时更新数据库内容,利用限流阈值时从数据库中查问限流阈值。你也能够把这个配置放到任何其它中央,比方Consul、Redis,甚至配置文件中。

解决架构

为了形容的更清晰,我这里提供一张图:

如上图所示,业务服务集成了限流性能,外围模块有两个:

  • 限流解决:这个间接集成FireflySoft.RateLimit.AspNetCore即可实现。
  • 监控配置变更:这是独自扩大的局部,次要逻辑是:读取数据库中的限流规定配置,如果有变动,则调用FireflySoft.RateLimit的限流规定更新接口。

其它模块:

  • 结构谬误:这个也是FireflySoft.RateLimit.AspNetCore自带的性能,能够自定义错误码和谬误音讯内容。
  • 限流配置更改程序:这里没有实现。性能就是更改数据库中的限流规定配置,咱们测试间接改数据库就行了。

编写代码

这里写了一个基于.Net6 的 WebAPI demo,我的项目构造如下图,你也能够间接点开查看:samples/aspnetcore6 (github.com)

为了不便集成到本人的我的项目中,这里也写一下具体的应用步骤:

1、创立或关上你的我的项目

关上我的项目,你能够用Visual Studio,也能够用Visual Studio Code。

如果我的项目是.NET Framework,必须是4.6.1及以上。如果是.NET Core,必须是2.0及以上。这里是.NET6。

2、装置Nuget包

你能够应用Package Manager:

Install-Package FireflySoft.RateLimit.AspNetCore -Version 2.0.2-rc1

也可应用.NET CLI:

dotnet add package FireflySoft.RateLimit.AspNetCore --version 2.0.2-rc1

3、配置数据库表

你须要有一个MySQL,我倡议是5.7及以上,下边是创立表和测试配置的SQL脚本:

CREATE TABLE `rate_limit_rule` (  `Id` varchar(40) NOT NULL,  `Path` varchar(100) NOT NULL,  `PathType` int(11) NOT NULL,  `TokenCapacity` int(11) NOT NULL,  `TokenSpeed` int(11) NOT NULL,  `AddTime` datetime NOT NULL,  `UpdateTime` datetime NOT NULL,  PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4INSERT INTO rate_limit_rule (Id,`Path`,PathType,TokenCapacity,TokenSpeed,AddTime,UpdateTime) VALUES     ('1','/WeatherForecast/GetToday',1,26,20,'2021-11-16 00:00:00.0','2021-11-16 00:00:00.0'),     ('2','/WeatherForecast/GetTomorrow',1,13,10,'2021-11-16 00:00:00.0','2021-11-16 00:00:00.0'),     ('3','All',2,29,25,'2021-11-16 00:00:00.0','2021-11-16 00:00:00.0');

关上我的项目中的appsettings.json,增加一个DbConn的配置项:

{  "DbConn":"Server=127.0.0.1;User ID=root;Password=l123456;port=3306;Database=ratelimit;CharSet=utf8mb4;",  ...}

这里边的数据库地址、数据库名称、帐号密码、字符集都须要改成本人的。

你也能够应用其它的数据库连贯配置形式,比方放到Consul中,或者写到本人的配置核心,甚至写死在代码中。

4、编写”监控配置变更“

在上边的架构图中,提到一个”监控配置变更“的局部,这个是这篇文章的重头戏。FireflySoft.RateLimit本身没有提供这部分,须要依据需要本人实现。我这里提供一个实现计划,仅供参考。

这个局部我写了5个文件:

  • RateLimitRuleDAO.cs:实现从数据库查问出限流规定配置。
  • RateLimitConfigurationManager.cs:实现跟踪数据库中的限流配置变更,如果有变更则触发一个事件。
  • NonCapturingTimer.cs:用于定时查询数据库中的限流配置。不捕获上下文的Timer,用习惯了而已。
  • AutoUpdateAlgorithmManager.cs:注册事件到RateLimitConfigurationManager中,事件产生时更新到限流算法中。
  • AutoUpdateAlgorithmService.cs:不便注册服务:向ASP.NET Core中注册上边这几个服务。

代码量比拟大,这里就不贴了,能够到Github上查看具体。

5、注册服务和应用中间件

.NET6中這局部要写到Program.cs中,限于篇幅,这里省略了很多代码,只须要关注如下几行:

  • builder.Services.AddAutoUpdateRateLimitAlgorithm 这个在AutoUpdateAlgorithmService.cs中定义的。
  • builder.Services.AddRateLimit 这个是FireflySoft.RateLimit.AspNetCore定义的。
  • app.UseRateLimit() 这个是FireflySoft.RateLimit.AspNetCore定义的。
using aspnetcore6.RateLimit;using FireflySoft.RateLimit.AspNetCore;var builder = WebApplication.CreateBuilder(args);...// Add firefly soft rate limit servicebuilder.Services.AddAutoUpdateRateLimitAlgorithm();builder.Services.AddRateLimit(serviceProvider =>{    var algorithmManager = serviceProvider.GetService<AutoUpdateAlgorithmManager>();    if (algorithmManager != null)    {        return algorithmManager.GetAlgorithmInstance();    }    return null;});var app = builder.Build();...// Use firefly soft rate limit middlewareapp.UseRateLimit();app.MapControllers();app.Run();

6、启动服务并测试

能够应用Postman来运行一个Runner,执行100次,看看实际效果。

对于.NET6

尽管题目中提到了.NET6,不过到目前为止还没看到什么对于.NET6的特地内容,所以这里特地筹备了一点对于.NET6的内容,否则就太题目党了。

如果你应用过.NET Core,其实.NET6用起来也没有太多变动,很多.net core、.net standard的库也都兼容,这里列举两点我感觉变动比拟大的中央:

Namespace

当初namespace能够间接申明利用到整个文件,不须要再加大括号,被括号层级折磨的人轻松了。

using System.Collections.ObjectModel;namespace aspnetcore6.RateLimit;public class RateLimitConfiguration{    public string? Path { get; set; }    public LimitPathType PathType { get; set; }    public int TokenCapacity { get; set; }    public int TokenSpeed { get; set; }}

Top-level statements

Program.cs和Startup.cs的内容合并到Program.cs中了,并且不须要显式编写main办法,间接一行行的写就行了。这样的确又简便了一些。次要内容还是那两局部:构建Web利用(注册服务、应用中间件)、运行Web利用。

var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();...var app = builder.Build();...app.MapControllers();app.Run();

不过一个利用中只能有一个这样的文件,你也不能再写其它main办法作为程序的入口点。


FireflySoft.RateLimit 是一个基于 .NET Standard 的限流类库,其内核简略笨重,可能灵便应答各种需要的限流场景。

其次要特点包含:

  • 多种限流算法:内置固定窗口、滑动窗口、漏桶、令牌桶四种算法,还可自定义扩大。
  • 多种计数存储:目前反对内存、Redis两种存储形式。
  • 分布式敌对:通过Redis存储反对分布式程序对立计数。
  • 限流指标灵便:能够从申请中提取各种数据用于设置限流指标。
  • 反对限流惩办:能够在客户端触发限流后锁定一段时间不容许其拜访。
  • 动静更改规定:反对程序运行时动静更改限流规定。
  • 自定义谬误:能够自定义触发限流后的错误码和谬误音讯。
  • 普适性:原则上能够满足任何须要限流的场景。

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

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