rate.limiter必须复用,不能每请求新建;应按ip、用户id或路径前缀等维度共享实例,避免key泛滥,并在http中间件中优先使用wait(ctx)配合请求上下文超时控制。

rate.Limiter 必须复用,不能每请求 new 一个
直接在 http.HandlerFunc 或 Gin/Echo 的中间件闭包里调用 rate.NewLimiter,等于给每个请求配了个独立水桶——burst 归零、速率失效,限流完全不生效。它不是“开关”,而是有状态的令牌生成器,必须跨请求共享实例。
常见错误写法:
❌ func(w http.ResponseWriter, r *http.Request) { limiter := rate.NewLimiter(10, 5); limiter.Wait(r.Context()) }
✅ 正确做法:定义为包级变量、注入到 handler 结构体,或用 sync.Map 按 key 缓存(如按 IP 或用户 ID)。
注意:rate.Limiter 本身是线程安全的,但复用不等于“全局单例”。全站共用一个实例 = 所有接口、所有用户共享同一套配额,这通常不是你想要的。
按请求特征分桶:IP、用户 ID、路径前缀怎么选 key
限流维度错了,再准的算法也白搭。核心原则是「谁该被一起限,谁就共享同一个 limiter 实例」。
- 按
r.RemoteAddr限流最简单,但 CDN 或反向代理后会变成同一个源 IP;生产环境建议解析X-Forwarded-For并做可信校验,或结合真实客户端 IP 提取逻辑 - 按用户标识(如
r.Header.Get("X-User-ID"))更精准,但需确保 header 不可伪造;匿名用户可 fallback 到 IP 哈希(fnv.Sum64String(ip)) - 按路径前缀(如
/api/pay、/api/login)适合分级管控:支付接口严控 5 QPS,登录接口宽松 50 QPS
key 泛滥是高频坑:攻击者发一堆随机 X-User-ID,sync.Map 就无限膨胀。务必加约束——比如只接受 32 字符内、仅含字母数字的 ID,并配后台 goroutine 定期清理 5 分钟未访问的条目。
Wait() vs Allow():HTTP 中间件里该用哪个
绝大多数 API 限流场景,应该用 limiter.Wait(ctx),而不是 limiter.Allow()。
Allow() 只查“此刻有没有令牌”,返回 true 后,其他 goroutine 可能瞬间抢走它,导致实际超限;它适合非关键路径(如埋点上报),或前置快速拒绝(但得自己算 Retry-After)。
Wait(ctx) 才是真正意义上的“排队等位”:它会阻塞直到拿到令牌,或上下文超时(如客户端设了 3s timeout)。它自动尊重 ctx.Done(),不会卡死 goroutine。
务必传入 r.Context()(net/http)或 c.Request.Context()(Gin),别用 context.Background() —— 否则超时控制彻底失效,连接堆积,服务雪崩。
burst 参数设多少才合理
burst 是令牌桶的“最大容量”,不是“突发上限”,设太小或太大都会出问题。
设为 1:退化成严格匀速,真实流量天然有毛刺,连用户双击按钮都可能被拒;
设为 1000:相当于没限流,桶永远满着,burst 失去意义。
经验值是 QPS 的 2–5 倍:
• 10 QPS → burst=20~50
• 100 QPS → burst=200~300
• 登录接口可适当放宽(允许重试),支付接口应收紧(防暴力试探)
burst 还影响冷启动行为:新 limiter 初始化时桶是满的,burst 越大,初始突发容忍越强。但别指望靠它扛住持续压测——长期速率仍由 rate.Limit 决定。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











