自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...
播种更多架构常识,请关注公众号 萤火架构。原创内容,转载请注明出处。