.NET 8+ 官方推荐用 Microsoft.Extensions.Http.Resilience 的 RateLimiterStrategy 实现令牌桶限流,需通过 AddResilienceHandler 挂载到 HttpClient 才生效;分布式场景需配合 IDistributedCache,如 RedisRateLimiter。

用 Microsoft.Extensions.Http.Resilience 做令牌桶限流最省事
.NET 8+ 官方推荐方式不是手写算法,而是直接用 ResiliencePipeline 配合 RateLimiterStrategy。它底层封装了线程安全的令牌桶,支持分布式场景(需配 IDistributedCache)。
常见错误是只注册策略却不挂载到 HttpClient:限流器必须通过 AddResilienceHandler 注入调用链,否则完全不生效。
- 安装包:
Microsoft.Extensions.Http.Resilience(注意不是旧版Polly) - 注册示例:
services.AddHttpClient<myapiclient>() .AddResilienceHandler("api-rate-limit", builder => builder.AddRateLimiter(new RateLimiterStrategyOptions { PermitLimit = 100, QueueLimit = 20, Window = TimeSpan.FromMinutes(1) }));</myapiclient> - 不配
QueueLimit会导致超限请求立刻失败(503 Service Unavailable),设为 0 表示禁用排队 - 该策略默认使用内存计数器,多实例部署时需手动替换为
RedisRateLimiter或自定义IRateLimiter
手写滑动窗口要小心时间分片精度和内存泄漏
滑动窗口比令牌桶更适合统计类限流(比如“最近60秒内最多100次”),但自己实现容易在高并发下出错:窗口切片太粗导致误放行,切片太细则内存暴涨。
关键点是窗口不能用 Dictionary 存每秒计数——时间戳精度一旦到毫秒,键会爆炸式增长。
- 正确做法:按固定步长(如 100ms)做桶,用
ConcurrentDictionary存桶 ID(timestamp / 100) - 每次请求先清理过期桶:
bucketKey ,再累加当前桶 - 总请求数 = 所有未过期桶的值之和,别用
.Values.Sum(),要遍历并跳过已过期桶 - 示例片段:
var bucketId = DateTimeOffset.UtcNow.Ticks / 10_000_000; // 100ms 桶 var windowStart = bucketId - (long)(600 / 100); // 60秒窗口 int total = 0; foreach (var kvp in _buckets) if (kvp.Key >= windowStart) total += kvp.Value;
AspNetCore.RateLimiting 包对中间件友好但不支持细粒度路由匹配
如果你用的是 MVC/Web API 中间件模式,AspNetCore.RateLimiting(微软官方维护)比手写更稳妥,它自动集成到 RequestDelegate 链路,且支持基于 IP、客户端 ID、路由模板的策略。
但它默认不解析 [Route] 中的参数,比如 /api/users/{id} 和 /api/users/123 被视为不同路由,导致限流失效。
- 解决办法:自定义
IRateLimitPolicy,重写ResolveIdentityAsync,从HttpContext提取RouteData.Values后标准化路径 - 注意
SetIsTrustedProxy必须开启,否则 X-Forwarded-For 头无效,所有请求都算作同一个 IP - 内存存储(
MemoryCache)下,重启后计数清零;Redis 存储需额外引用Microsoft.Extensions.Caching.StackExchangeRedis - 错误响应体是固定 JSON:
{"error": "API calls quota exceeded!"},想改文案得替换整个RateLimitMiddleware
令牌桶和滑动窗口选哪个?看你的失败容忍度
令牌桶允许短时突发(burst),适合保护后端服务;滑动窗口严格卡时间窗,适合防刷、计费等强一致性场景。
但别混用:一个接口同时配两种策略,可能因执行顺序导致行为不可预测——中间件顺序、策略注册时机、缓存 key 冲突都会引发漏限或双倍拦截。
- 突发流量容忍高 → 选令牌桶(
RateLimiterStrategy) - 需要精确到秒级统计 → 选滑动窗口(自己实现或用
AspNetCore.RateLimiting的SlidingWindowRateLimiter) - 别在 Action 里手动调用
TryAcquire:这绕过中间件生命周期,无法统一记录日志、返回标准错误码 - 压测时重点看
RateLimit-Remaining和X-RateLimit-Limit响应头是否实时更新,这是验证限流生效的最直接证据
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











