C# API速率限制方法 C# ASP.NET Core如何实现Rate Limiting

2次阅读

ASP.NET Core 6+ 内置 AddRateLimiter 是首选方案,需正确注册服务、启用中间件、统一策略名,并按需选择 SlidingwindowLimiter 或 TokenBucketLimiter;自定义 KeyGenerator 可实现用户级限流;生产环境须用 redisRateLimiter 并开启调试日志与响应头。

C# API速率限制方法 C# ASP.NET Core如何实现Rate Limiting

ASP.NET Core 6+ 内置 AddRateLimiter 是首选方案

ASP.NET Core 6 开始原生支持速率限制,无需第三方包(如 AspNetCoreRateLimit),配置更轻、扩展性更好。它基于策略(RateLimiterOptions)和限流器(如 SlidingWindowLimiterTokenBucketLimiter)实现,与中间件生命周期深度集成。

常见错误是直接在 Program.cs 中调用 AddRateLimiter 后忘记启用中间件,或策略名称拼写不一致导致 429 不触发。

  • 必须在 app.UseRateLimiter() 之前注册服务,且顺序不能颠倒
  • 策略名(如 "sliding")需与 RequireRateLimiting("sliding") 中的字符串完全一致
  • SlidingWindowLimiter 更适合突发流量控制;TokenBucketLimiter 更适合平滑匀速放行
  • 限流键(KeyGenerator)默认按客户端 IP,若需按 API key 或用户 ID,必须自定义 HttpContext 提取逻辑

SlidingWindowLimiter 配置关键参数说明

滑动窗口是最常用策略,但参数含义容易误解:PermitLimit 是窗口内允许请求数,Window 是窗口时长,而 QueueProcessingorderQueueLimit 控制排队行为——不是所有场景都需要排队。

  • PermitLimit = 100 + Window = TimeSpan.FromMinutes(1) 表示每分钟最多 100 次请求,不是“每 60 秒清零”,而是滚动统计
  • 设置 QueueLimit = 10 后,超出限流的请求会排队,但超时由 QueueTimeout 控制(默认 10 秒),超时仍返回 429
  • 若禁用排队(QueueLimit = 0),所有超额请求立即 429,响应更快,适合低延迟敏感接口
  • ReplenishmentPeriod 仅对 TokenBucketLimiter 有效,滑动窗口中设了也无效

如何按用户身份(而非 IP)做限流

默认限流键只取 HttpContext.Connection.Remoteipaddress,要切换到用户维度,必须重写 KeyGenerator,并在认证后读取用户标识。JWT 或 cookie 认证均可支持,但要注意未认证请求的 fallback 处理。

  • 在策略配置中传入自定义 KeyGenerator 函数,例如提取 HttpContext.User.FindFirst("sub")?.Value
  • 未登录用户建议 fallback 到 IP,避免匿名用户共享同一限流桶:用 HttpContext.User.Identity.IsAuthenticated ? userId : ip
  • 若使用 [Authorize] 特性,确保 UseAuthentication()UseRateLimiter() 之前调用,否则 User 为空
  • 注意:User 对象在限流中间件执行时已解析完成,但自定义声明需在 AddJwtBearerAddCookie 中显式映射

生产环境必须检查的三个隐藏问题

本地测试通过不代表线上可用。分布式部署、高并发压测、日志埋点缺失,常让限流失效却不报错。

  • 默认内存限流器(MemoryRateLimiter)不支持多实例共享状态,K8s 多 Pod 场景下必须换用 RedisRateLimiter 并注入 IConnectionMultiplexer
  • 未开启详细日志时,限流拒绝不会输出任何信息,建议在 Program.cs 中配置:builder.Logging.AddConsole().AddFilter("microsoft.AspNetCore.RateLimiting", LogLevel.Debug)
  • 前端可能忽略 X-RateLimit-Remaining 等响应头,但这些头默认不启用,需手动调用 EnableRateLimitHeaders(true) 才会写入

限流策略本身不难配,真正复杂的是键的设计粒度、跨进程状态同步、以及拒绝时的用户体验衔接——比如是否返回友好的 jsON 错误体,而不是裸 429 html 页面。

text=ZqhQzanResources