前言
这个需求是这样的,我之前做了个工单系统,现在要对登录、注册、发起工单这些功能做限流,不能让用户请求太频繁。
然后我找到了这个 AspNetCoreRateLimit 组件,在 Github 上有接近三千个星星,看了一下文档使用也简单灵活,于是决定尝试一下~
AspNetCoreRateLimit 组件
这是官方的介绍:
The AspNetCoreRateLimit NuGet package contains an IpRateLimitMiddleware and a ClientRateLimitMiddleware, with each middleware you can set multiple limits for different scenarios like allowing an IP or Client to make a maximum number of calls in a time interval like per second, 15 minutes, etc. You can define these limits to address all requests made to an API or you can scope the limits to each API URL or HTTP verb and path。
AspNetCoreRateLimit是一个ASP.NET Core速率限制解决方案,旨在基于IP地址或客户端ID控制客户端对Web API或MVC应用程序发出请求的速率。
IpRateLimitMiddleware和一个ClientRateLimitMiddleware,每个中间件都可以为不同的场景设置多个限制,比如允许IP或客户端在时间间隔内进行最大数量的调用,比如每秒、15分钟等。您可以定义这些限制以处理对API发出的所有请求,也可以将限制范围限定为每个API URL或HTTP动词和路径。
AspNetCore配置 里定义规则,意味着可以不重新编译程序就修改限流规则,官方给的例子是直接在 appsettings.json
里配置,但使用其他配置源理论上也没问题(配置中心用起来)。
首先它有两种模式:
- 根据IP地址限流
- 根据 ClientID 限流
X-ClientId,这个要自己实现,可以直接用用户ID。
RateLimit 组件可以配置全局的限流,也可以配置对某个IP地址(段进行限流。
配置服务
appsettings.json 读取数据,先在 Program.cs
注册配置服务
builder.Services.AddOptions(;
然后写个扩展方法来注册 RateLimit 的相关服务
using AspNetCoreRateLimit;
using AspNetCoreRateLimit.Redis;
using StackExchange.Redis;
写个静态类
public static class ConfigureRateLimit {
public static void AddRateLimit(this IServiceCollection services, IConfiguration conf {
//load general configuration from appsettings.json
services.Configure<IpRateLimitOptions>(conf.GetSection("IpRateLimiting";
var redisOptions = ConfigurationOptions.Parse(conf.GetConnectionString("Redis";
services.AddSingleton<IConnectionMultiplexer>(provider => ConnectionMultiplexer.Connect(redisOptions;
services.AddRedisRateLimiting(;
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(;
}
public static IApplicationBuilder UseRateLimit(this IApplicationBuilder app {
app.UseIpRateLimiting(;
return app;
}
}
来解析一下配置的代码。
所以直接用 IpRateLimitOptions
services.Configure<IpRateLimitOptions>(conf.GetSection("IpRateLimiting";
要做根据IP限流,就得记录每个IP访问了多少次,RateLimit 组件支持多种存储方式,最简单的可以直接存内存里,不过为了稳定我还是选择 Redis。
var redisOptions = ConfigurationOptions.Parse(conf.GetConnectionString("Redis";
services.AddSingleton<IConnectionMultiplexer>(provider => ConnectionMultiplexer.Connect(redisOptions;
services.AddRedisRateLimiting(;
最后注入一下 IRateLimitConfiguration
,我猜应该是中间件要用到的。至少我目前在 Controller 代码里不需要用到任何跟 RateLimit 有关的代码。
Program.cs
注册服务
builder.Services.AddRateLimit(builder.Configuration;
添加中间件
var app = builder.Build(;
app.UseExceptionless(;
app.UseStaticFiles(new StaticFileOptions {
ServeUnknownFileTypes = true
};
app.UseRateLimit(;
// ...
app.Run(;
我这里把 UseRateLimit
放在 UseStaticFiles
后面,不然页面里的静态文件都被算进去访问次数,很快就被限流了。
配置
appsettings.json 里写具体的限流规则。
-
StackBlockedRequests
- 按照默认的设置为 false 就行,设置成 true 的话,一个接口被限流之后再重复请求还会计算到访问次数里面,这样有可能导致限流到天荒地老。
EnableEndpointRateLimiting
- 这个选项要设置为 true,不然设置的限流是全局的,不能根据某个路径单独设置限流
其他的配置顾名思义,懂的都懂。
GeneralRules 是对具体路径的限流规则
EnableEndpointRateLimiting 设置为 false 的话,那就这样设置,1分钟只能访问5次
{
"Endpoint": "*",
"Period": "1m",
"Limit": 5
}
Endpoint
可以设置HTTP方法:路径
的形式,比如post:/account/login
具体看文档吧(参考文档第三条){ "IpRateLimiting": { "EnableEndpointRateLimiting": true, "StackBlockedRequests": false, "RealIpHeader": "X-Real-IP", "ClientIdHeader": "X-ClientId", "HttpStatusCode": 429, "IpWhitelist": [], "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ], "ClientWhitelist": [ "dev-id-1", "dev-id-2" ], "GeneralRules": [ { "Endpoint": "*:/ticket/add", "Period": "1m", "Limit": 5 }, { "Endpoint": "post:/account/login", "Period": "1m", "Limit": 5 }, { "Endpoint": "post:/account/SignUp", "Period": "1m", "Limit": 5 } ], "QuotaExceededResponse": { "Content": "{{ \"message\": \"先别急,你访问得太快了!\", \"details\": \"已经触发限流。限流规则: 每 {1} 只能访问 {0} 次。请 {2} 秒后再重试。\" }}", "ContentType": "application/json", "StatusCode": 429 } } }
同时自定义了被限流时的提示。
效果如下
{ "message": "先别急,你访问得太快了!", "details": "已经触发限流。限流规则: 每 1m 只能访问 5 次。请 16 秒后再重试。" }
参考资料
- https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/
- https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-7.0
- https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/IpRateLimitMiddleware