go中api网关限流应优先用rate.limiter的reserve而非allow,burst设为qps的2–3倍;分布式场景需redis原子脚本实现共享状态;key设计须归一化并校验来源,失败时降级到本地令牌桶。

Go 里用 golang.org/x/time/rate 实现令牌桶,别直接 new Limiter 就完事
令牌桶最常用、也最容易误用。很多人一上来就写 rate.NewLimiter(10, 5),以为每秒放行 10 个请求、初始容量 5,但没意识到:Limiter 的 Allow 和 Reserve 行为差异极大,API 网关里必须用 Reserve 才能准确控住并发节奏。
常见错误现象:QPS 明显超标、突发流量打穿后端、日志里一堆 rate: wait longer than allowed 却没被拦截。
-
Allow是“查一下现在能不能过”,不阻塞、不预留,适合后台任务节流,不适合网关入口 -
Reserve返回一个*rate.Reservation,调用Delay或OK才真正消耗令牌;网关应在http.Handler中先Reserve,再根据OK()决定是否继续处理 - 注意
burst(桶容量)不能设太小,否则无法吸收短时毛刺;也不能太大,否则限流形同虚设;建议设为期望 QPS 的 2–3 倍 - 如果用在 HTTP 中间件,记得把
Reservation绑定到context.Context,避免 defer 里调用Cancel失效
漏桶在 Go 里没有标准库支持,手写要注意时间精度和 goroutine 泄漏
标准库没提供漏桶,有人用 time.Ticker + channel 模拟,结果发现延迟不准、高并发下卡顿、甚至 goroutine 积压。根本原因是漏桶本质是“固定速率出水”,而 Ticker 的 tick 时间受调度影响,且每个桶单独启 goroutine 成本太高。
使用场景:需要严格平滑输出(比如向下游推送日志、调用低频第三方 API),且不允许任何突发。
立即学习“go语言免费学习笔记(深入)”;
- 别为每个用户/租户起独立
Ticker—— 1000 个租户 = 1000 个 goroutine,GC 压力大,且Ticker.Stop容易漏调 - 推荐用单个全局
time.Ticker驱动共享队列,配合sync.Map存各桶的剩余水量和上次漏水时间 - 每次请求来时,先按时间差计算应漏掉多少(
elapsed.Seconds() * rate),再判断是否可通行;浮点运算要小心精度,建议用纳秒+整数运算 - 注意:漏桶无法应对瞬时突增,API 网关中纯漏桶不如令牌桶灵活,常和令牌桶混用(如外层漏桶保底、内层令牌桶容错)
分布式场景下,rate.Limiter 本地内存失效,必须外接存储做协调
单机 rate.Limiter 在多实例网关下完全无效——每个节点各自计数,总 QPS 是实例数 × 限流值。想真正分布式限流,核心是“共享状态 + 原子操作”,不是换算法。
常见错误现象:加了限流配置,压测却发现整体 QPS 翻倍、三台机器一起打崩下游。
- Redis 是最常用选择,但别直接用
INCR+EXPIRE—— 有竞态;要用EVAL脚本保证原子性,例如 Lua 脚本一次性完成“读当前值、判断、incr、设 TTL” - Redis 集群模式下注意 key hash 到同一 slot,否则
EVAL跨 slot 报错;建议 key 加前缀如rate:api:/user/profile:{uid} - 性能敏感场景慎用 Redis:一次限流至少 1 次 RTT;可考虑本地 LRU cache + 定期回源(如 1s 刷新),但需接受小概率超发
- 别忽略失败降级:Redis 不可用时,要么放行(业务可容忍),要么 fallback 到本地令牌桶(防止雪崩),不能直接 panic 或阻塞
API 网关里限流 key 的设计,决定你能不能按用户/接口/IP 精准控制
限流效果好不好,一半看算法,一半看 key。key 设错,限流就变成“随机丢包”。Go 服务里常犯的错是把 key 拼成字符串时不转义、不归一化,导致相同用户被算成多个桶。
使用场景:按 Authorization token 限流、按 X-Forwarded-For 限流、按路由路径 + 查询参数组合限流。
- 别直接用原始
r.RemoteAddr做 IP key —— 可能是代理内网地址;优先取X-Real-IP或X-Forwarded-For第一段,并校验可信代理列表 - 带 query 参数的 URL 限流,必须先规范排序(如按 key 字典序)、去除空值、解码后再拼 key,否则
/search?q=a&b=1和/search?b=1&q=a被当成两个 key - JWT 用户限流,别解析整个 token —— 开销大;提取
sub或client_id字段即可,且确保该字段不可伪造(即必须由网关自身验证过) - key 长度别超过 1KB,Redis 对 key 长度有限制,过长还影响哈希分布;建议用
sha256(key)[:16]截断,但注意碰撞风险极低,可接受
真正难的从来不是选漏桶还是令牌桶,而是 key 怎么定义、失败怎么兜底、监控指标从哪埋 —— 这些地方不动手试三次,光看文档永远踩坑。










