.NET如何实现限流?ASP.NET Core RateLimiter中间件使用教程
2026-10-01 1 0
在 Web API 开发中,接口可能遇到短时间大量请求,例如登录接口被频繁调用、公开 API 被高频访问、某个查询接口突然出现流量峰值。ASP.NET Core 已经提供 Microsoft.AspNetCore.RateLimiting,可以直接在应用层配置请求速率限制。
RateLimiter 支持固定窗口、滑动窗口、令牌桶和并发限制四种常见算法,可以针对整个应用,也可以单独限制某些 API。
注册RateLimiter服务
在 Program.cs 中调用 AddRateLimiter:
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("api", limiterOptions =>
{
limiterOptions.PermitLimit = 10;
limiterOptions.Window = TimeSpan.FromMinutes(1);
limiterOptions.QueueLimit = 0;
});
});
var app = builder.Build();
app.UseRateLimiter();
app.MapGet("/api/test", () =>
{
return Results.Ok("请求成功");
}).RequireRateLimiting("api");
app.Run();
上面的配置表示 /api/test 每分钟最多允许 10 个请求,超过限制的请求会被拒绝。
ASP.NET Core 8 开始,使用 RateLimiter 中间件时需要调用 AddRateLimiter 注册相关服务,否则应用启动时会出现服务未注册错误。
固定窗口限流
AddFixedWindowLimiter 是比较容易理解的一种方式。
例如:
options.AddFixedWindowLimiter("fixed", opt =>
{
opt.PermitLimit = 100;
opt.Window = TimeSpan.FromMinutes(1);
opt.QueueLimit = 0;
});
表示一个时间窗口允许 100 次请求。窗口结束后重新计算。
它适合普通查询接口、公开 API 等场景。如果接口流量比较稳定,固定窗口配置起来比较简单。
滑动窗口限流
滑动窗口可以减少固定窗口切换时出现的流量突增问题:
builder.Services.AddRateLimiter(options =>
{
options.AddSlidingWindowLimiter("sliding", opt =>
{
opt.PermitLimit = 100;
opt.Window = TimeSpan.FromMinutes(1);
opt.SegmentsPerWindow = 6;
opt.QueueLimit = 0;
});
});
这里把一分钟划分为 6 个时间段,限制器会根据滑动窗口中的请求数量进行判断。微软官方提供了 AddSlidingWindowLimiter 对应的配置方式。
令牌桶限流
令牌桶适合需要允许短时间突发流量的 API:
builder.Services.AddRateLimiter(options =>
{
options.AddTokenBucketLimiter("token", opt =>
{
opt.TokenLimit = 20;
opt.TokensPerPeriod = 5;
opt.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
opt.QueueLimit = 0;
});
});
可以把令牌理解成接口调用额度。请求消耗令牌,系统按照设定周期补充令牌。短时间内积累的令牌可以处理一定程度的突发请求。
按照IP地址限流
实际项目中,经常需要给不同客户端分别计算请求次数。这时可以使用分区限流。
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter =
PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
var ip = context.Connection.RemoteIpAddress?.ToString()
?? "unknown";
return RateLimitPartition.GetFixedWindowLimiter(
ip,
_ => new FixedWindowRateLimiterOptions
{
PermitLimit = 60,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 0
});
});
});
这样每个 IP 都拥有独立的请求计数器,一个客户端达到 60 次限制后,不会直接消耗其他客户端的额度。
RateLimiter 的分区键还可以使用用户 ID、API Key 等数据。
限制并发请求数量
有些接口执行时间比较长,例如导出 Excel、生成报表、复杂数据库查询。这种情况下,单纯限制一分钟请求数量可能不够,可以使用并发限制:
builder.Services.AddRateLimiter(options =>
{
options.AddConcurrencyLimiter("concurrent", opt =>
{
opt.PermitLimit = 10;
opt.QueueLimit = 5;
opt.QueueProcessingOrder =
QueueProcessingOrder.OldestFirst;
});
});
这里最多允许 10 个请求同时执行,额外请求最多排队 5 个。
并发限制关注的是“同时运行多少个请求”,固定窗口、滑动窗口和令牌桶关注的是“一段时间允许多少个请求”。
超过限制返回429
生产环境通常建议把被限流的请求返回 HTTP 429:
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddFixedWindowLimiter("api", opt =>
{
opt.PermitLimit = 10;
opt.Window = TimeSpan.FromMinutes(1);
opt.QueueLimit = 0;
});
});
也可以通过 OnRejected 自定义响应内容,并根据限制器提供的 Retry-After 信息告诉客户端什么时候重试。
项目中怎么选择限流方式
普通 API 可以从固定窗口开始配置,例如每个用户每分钟 60 次。接口存在明显突发流量时,可以考虑令牌桶。需要控制数据库、CPU 密集型接口的同时执行数量时,可以使用并发限制。对流量连续性要求较高的场景,可以选择滑动窗口。还可以给不同接口配置不同策略,例如登录接口限制得更严格,普通查询接口设置较高额度,文件导出接口单独限制并发数。
RateLimiter 本身属于应用层限流。如果应用部署了多个实例,还需要考虑各实例之间的限流计数是否需要统一,单机内存计数并不能自动变成整个集群的统一计数器。
总结
ASP.NET Core RateLimiter 已经覆盖了 Web API 中常见的限流需求。基本使用流程就是通过 AddRateLimiter 注册策略,调用 UseRateLimiter 启用中间件,再使用 RequireRateLimiting 将策略应用到指定接口。
实际项目不要直接照搬某个固定数字。应结合接口访问量、数据库压力、CPU 使用率和正常用户行为进行压测,再确定 PermitLimit、窗口时间、队列数量等参数。微软官方也建议上线前对限流配置进行充分测试。