共计 5202 个字符,预计需要花费 14 分钟才能阅读完成。
自 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=utf8mb4
INSERT 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 service
builder.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 middleware
app.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…
播种更多架构常识,请关注公众号 萤火架构。原创内容,转载请注明出处。