rate.limiter是go官方基于令牌桶算法的线程安全限流器,需复用实例而非每请求新建;应按用户/ip分桶避免锁竞争,http中间件中须在路由匹配后、业务前调用wait()并传入request context。
用 golang.org/x/time/rate 实现令牌桶限流
Go 官方扩展库 rate.Limiter 是最常用、最轻量的限流方案,底层基于令牌桶算法,线程安全,适合 HTTP 中间件场景。
常见错误是直接在 handler 里 new 一个 rate.Limiter,导致每个请求都用独立限流器,完全失效。正确做法是复用同一个实例:
- 按用户 ID 或 IP 做 key 分桶限流?需配合
sync.Map或第三方缓存(如ristretto),避免全局锁瓶颈 -
rate.Every(1 * time.Second)表示每秒放行 1 个令牌;rate.Limit(10)表示每秒最多 10 次——两者等价,但前者更直观 - 注意
WaitN(ctx, n)的n是请求数量(比如批量接口),不是并发数;单次请求固定传1
HTTP 中间件中嵌入限流逻辑
限流必须在路由匹配后、业务处理前触发,否则可能绕过或误杀健康请求。典型中间件写法如下:
func RateLimitMiddleware(limiter *rate.Limiter) gin.HandlerFunc {
return func(c *gin.Context) {
if err := limiter.Wait(c.Request.Context()); err != nil {
c.JSON(429, gin.H{"error": "too many requests"})
c.Abort()
return
}
c.Next()
}
}
关键点: