rate.limiter必须复用,每请求新建会导致限流失效;应按全局、ip、用户id或路径前缀维度共享实例,使用sync.map缓存并避免全局锁;限流须用wait(ctx)而非allow(),传入r.context()并设超时;token桶参数需匹配业务,burst不宜过小或过大;jwt鉴权必须在限流前执行。

rate.Limiter 必须复用,不能每请求 new 一个
限流失效的头号原因:在中间件闭包里直接 rate.NewLimiter。每个请求都新建实例,等于每人发一个无限水桶——burst 归零、速率归零,完全不控流。
正确做法是按维度共享实例:
- 全局统一限流(如全站总 QPS):定义为包级变量,比如
var globalLimiter = rate.NewLimiter(100, 20) - 按 IP 限流:用
sync.Map缓存,key 是客户端 IP(注意解析X-Forwarded-For并校验可信代理) - 按用户 ID(如从 JWT claims 提取):key 为
"user:" + userID,值为对应*rate.Limiter - 按路径前缀(如
/api/pay):key 为路径段,适合分级策略,支付接口严、查询接口松
别用全局大 map + mutex 锁——高并发下成瓶颈;sync.Map 读多写少时更稳。
Wait(ctx) 而不是 Allow(),且必须传入 request.Context()
Allow() 只查“此刻有没有 token”,返回 true 后可能瞬间被其他 goroutine 抢走,导致实际超限;它适合埋点、日志等非关键路径。
API 限流要的是排队等待、可控延迟,必须用 WaitN(ctx, n) 或 Wait(ctx):
- 务必传
r.Context(),不是context.Background()—— 否则客户端断连或网关超时,goroutine 会卡死 - 显式加超时:
ctx, cancel := context.WithTimeout(r.Context(), 300*time.Millisecond),避免阻塞太久 - 拒绝时返回标准
429 Too Many Requests,并设Retry-After: 1头
错误示例:limiter.Wait(context.Background());正确示例:limiter.WaitN(r.Context(), 1)。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
Token 桶参数别乱设:burst 和 r 的语义要对齐业务
rate.NewLimiter(r, b) 中,r 是「每秒令牌数」,b 是「桶初始容量」,不是「最大并发数」。
常见误配:
-
rate.NewLimiter(10, 1):每秒补 10 个,但桶只有 1 个容量 → 首次请求后立刻限流,突发流量全拒 -
rate.NewLimiter(1, 1000):补得慢、桶超大 → 等同于不限流 - 合理搭配:QPS=10 时,
b设为 20–50,允许短时突发;上传接口可设b=100配合单次消耗多个 token
别用 rate.Every(time.Second) 包裹数值——直接传 10.0 更清晰;rate.Every 仅用于构造 rate.Limit 类型。
JWT 验证和限流中间件的执行顺序不能错
先鉴权、再限流。如果把限流放在 auth 中间件之前,未认证用户也能耗尽配额,甚至用随机 X-User-ID 刷爆 sync.Map 导致内存泄漏。
典型链顺序应为:
- 日志中间件(最外层)
- JWT 验证中间件:解析
AuthorizationHeader,校验签名与exp,注入userID到context - 限流中间件:从
r.Context().Value("userID")取 ID,查sync.Map获取对应*rate.Limiter,再调WaitN - 业务 handler
注意:JWT 中间件失败时必须 return,否则后续中间件仍会执行;限流中间件拒绝后也必须 return,别漏掉 next.ServeHTTP 的跳过逻辑。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










