C# RateLimiter中间件方法 C#如何配置固定窗口和滑动窗口限流

来源:这里教程网 时间:2026-02-21 17:40:17 作者:

固定窗口限流用
RateLimitingMiddleware
配合
FixedWindowRateLimiter

ASP.NET Core 8+ 内置的限流中间件默认不启用任何策略,必须显式注册并配置。固定窗口的核心是“每 N 秒重置计数”,比如每 60 秒最多 100 次请求。

常见错误是只调用

AddRateLimiter
却没指定具体策略类型,导致运行时报
InvalidOperationException: No rate limiter policy is configured

Program.cs
中注册时,必须用
AddFixedWindowLimiter
并传入策略名(如
"fixed"
)和配置委托
Window
单位是
TimeSpan
,写成
TimeSpan.FromMinutes(1)
比硬写
60000
毫秒更安全
注意
PermitLimit
是窗口内总允许请求数,不是并发数;超限后默认返回 429,且不排队
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("fixed", options =>
    {
        options.Window = TimeSpan.FromMinutes(1);
        options.PermitLimit = 100;
        options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        options.QueueLimit = 0; // 不排队,直接拒绝
    });
});

滑动窗口限流需手动实现或换用第三方库

ASP.NET Core 原生

RateLimiter
目前(.NET 8 / 9 Preview)**不提供内置滑动窗口限流器**。官方只实现了
FixedWindowRateLimiter
SlidingWindowRateLimiter
的抽象基类,但后者未公开具体实现。

这意味着你不能像固定窗口那样直接调用

AddSlidingWindowLimiter
—— 它根本不存在于
RateLimiterOptions
扩展方法中。

若坚持用原生方案,只能基于
IServerRateLimiter
自定义实现,需维护时间分片(如 10 个 6 秒桶),处理跨桶聚合,逻辑复杂且易出错
更现实的做法是引入
AspNetCoreRateLimit
(已归档)或
Microsoft.Extensions.Resilience
中的
RateLimiter
(.NET 8+ 实验性 API,仍不稳定)
生产环境推荐用
StackExchange.Redis
+ Lua 脚本实现分布式滑动窗口,避免内存泄漏和节点间不同步

中间件启用时必须指定策略名,且顺序不能错

注册完策略只是第一步,中间件本身需要明确告诉它“对哪些请求应用哪个策略”。漏掉

UseRateLimiter
或策略名拼错,限流完全不生效,连日志都不会打。

UseRateLimiter()
必须放在
UseRouting()
之后、
UseEndpoints()
之前
策略名(如
"fixed"
)要和注册时完全一致,大小写敏感
若想按路由或 Header 区分策略,得用
ConfigureRateLimiter
+
EndpointRateLimiter
,而不是全局统一策略
app.UseRouting();
app.UseRateLimiter(); // 必须在这里
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers().RequireRateLimiting("fixed");
});

滑动窗口的“平滑”特性容易被误读为“更宽松”

滑动窗口不是简单地把固定窗口切碎再叠加,它的计数是动态滚动的:例如“每 60 秒最多 100 次”,第 59 秒发起的请求会同时计入第 1 秒和第 60 秒两个窗口片段,实际允许短时突发更高——但这不是 bug,是设计使然。

如果你发现滑动窗口比预期放行更多请求,先确认是否混淆了“窗口长度”和“滑动粒度”。比如 Redis 实现中用 6 秒分片 × 10 片 = 60 秒窗口,但每个请求只更新当前分片,查询时 sum 最近 10 个分片值。这个 sum 过程如果没原子执行,就会出现竞态漏计。

单机滑动窗口用
ConcurrentDictionary<datetime int></datetime>
加锁维护,性能差、GC 压力大,不建议
真正可靠的滑动窗口几乎都依赖外部存储(Redis、SQL Server 的行级锁 + TTL) 别为了“听起来高级”强行上滑动窗口——固定窗口在多数 API 场景下更稳定、更容易监控和调试

相关推荐