AspNetCoreRateLimit是.NET 6+ Web API全局限流最省事方案,支持内存/Redis存储、多维度限流,需正确注册缓存服务及中间件顺序。

用 AspNetCoreRateLimit 快速接入全局限流
这是最省事的方案,适合 .NET 6+ Web API 项目,开箱即用,不用自己写计数逻辑。它基于内存或 Redis 存储请求计数,支持按 IP、客户端 ID、路由等维度限流。
常见错误现象:InvalidOperationException: No service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache'——忘了注册缓存服务。
- 必须在
Program.cs中先调用builder.Services.AddMemoryCache()(内存模式)或AddStackExchangeRedisCache()(Redis 模式) -
AddAspNetCoreRateLimit()要在AddControllers()之后、Build()之前调用 - 中间件顺序很重要:必须在
UseRouting()之后、UseEndpoints()之前加UseClientRateLimiting()和UseIpRateLimiting() - 配置项如
GeneralRules中的period写成"1m"而不是"60s",只支持m/h/d单位
手写 ConcurrentDictionary + DateTimeOffset 实现轻量级令牌桶
当不想引入第三方包、且并发量中等(QPS
使用场景:内部管理后台的导出接口、定时任务触发端点、需快速灰度验证限流策略。
- 键建议用
$"{userId}:{endpoint}"或$"{ip}:{endpoint}",避免不同用户/IP 互相干扰 - 每次请求先
TryGetValue拿到上一次时间戳和剩余令牌;若过期(比如窗口 10 秒),重置为满令牌数 - 别直接用
DateTime.Now,要用DateTimeOffset.UtcNow避免时区或系统时间回拨问题 - 更新值必须用
AddOrUpdate原子操作,否则高并发下会漏统计
示例片段:
var key = $"{ip}:api/export";
var now = DateTimeOffset.UtcNow;
var bucket = _buckets.GetOrAdd(key, _ => new RateLimitBucket(10, TimeSpan.FromSeconds(10)));
if (bucket.TryConsume(now)) { /* 放行 */ } else { /* 返回 429 */ }
用 Microsoft.Extensions.Http.Resilience 对 HttpClient 限流
这不是给 API 接口限流,而是限制你代码里调用外部服务的频率——比如每秒最多调 5 次微信扫码回调接口,防止把对方打挂,也防自己被封。
容易踩的坑:AddStandardResilienceHandler() 默认不包含限流策略,得显式配 AddRateLimiter()。
- 必须在
AddHttpClient之后链式调用() ConfigurePrimaryHttpMessageHandler和AddStandardResilienceHandler -
AddRateLimiter的permitLimit是每窗口允许请求数,queueLimit是排队等待数,设为 0 表示拒绝排队直接失败 - 该限流作用于每个
IHttpClientFactory创建的客户端实例,不是全局共享,注意别为每个请求 new 一个 client - 不兼容 .NET 5 及更早版本,最低要求 .NET 6 +
Microsoft.Extensions.Http.Resilience8.0+
用中间件手动拦截 + IDistributedCache 做分布式限流
当部署在多台机器、又不想用 AspNetCoreRateLimit(嫌它配置太重或规则不够灵活),就得自己搭分布式的“计数器”。Redis 是最现实的选择。
性能影响明显:每次请求都走一次 Redis INCR + EXPIRE,压测时 RT 易升高,尤其网络抖动时。
- 别用
StringSet+ 自己解析 JSON 计数,要用 Redis 原生命令Eval脚本保证原子性(INCR+EXPIRE一起执行) - key 设计要带租户或环境前缀,比如
rate:prod:ip:192.168.1.100:/api/data,避免测试/生产数据混用 - 缓存过期时间必须严格大于限流窗口,否则可能刚过期就新请求进来,导致突增流量;建议设为窗口时间 × 1.2
- 记得捕获
RedisConnectionException并降级(比如放行),别让缓存故障拖垮整个接口
真正难的不是选哪种方式,而是想清楚“谁在调用”“失败后怎么反馈”“要不要透传限流头(X-RateLimit-Limit)”,这些细节比算法本身更容易出线上问题。











