直接用 golang.org/x/time/rate 配合 gin handlerfunc 即可实现轻量、可靠、无依赖的限流中间件,无需第三方库;因其官方维护、精度高、无锁、内存开销小,且支持灵活策略与细粒度控制。

直接用 golang.org/x/time/rate 配合 Gin 的 HandlerFunc 就能实现轻量、可靠、无依赖的限流中间件,不需要引入第三方限流库。
为什么不用第三方限流中间件?
很多项目一上来就找 gin-contrib/limiter 或 uber-go/ratelimit,但实际需求往往只是「每秒最多 10 次请求」这种基础场景。自己写中间件反而更可控:
-
golang.org/x/time/rate.Limiter是 Go 官方维护、经过生产验证的令牌桶实现,精度高、无锁、内存开销极小 - 第三方 gin 限流包常把 IP、路径、用户 ID 等逻辑耦合进中间件,导致无法按需组合(比如只想对
/api/pay限流,但不关心 IP) - 自带的
Limiter支持AllowN/ReserveN/WaitN三种模式,可灵活应对「拒绝」「排队」「降级」等策略
最简可用的全局 QPS 限流中间件
适用于所有路由统一限速(如全站 API 最多 100 QPS):
import "golang.org/x/time/rate"
func RateLimitMiddleware(r *rate.Limiter) gin.HandlerFunc {
return func(c *gin.Context) {
if !r.Allow() {
c.Header("X-RateLimit-Remaining", "0")
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "too many requests"})
return
}
c.Header("X-RateLimit-Remaining", strconv.FormatInt(int64(r.Burst()), 10))
c.Next()
}
}
// 使用:全局注册(每秒最多 10 次,突发允许 20 次)
r := rate.NewLimiter(rate.Every(time.Second/10), 20)
router.Use(RateLimitMiddleware(r))
注意:rate.Every(time.Second/10) 表示「每 100ms 放一个令牌」,即 10 QPS;Burst() 返回的是桶容量,不是剩余数 —— 所以这里用 r.Burst() 只是示意,真实剩余数需用 r.ReserveN 获取。
按 IP 或路径做细粒度限流的关键点
必须为每个维度(如 c.ClientIP() 或 c.Request.URL.Path)维护独立的 *rate.Limiter 实例,否则会互相干扰:
- 用
sync.Map缓存 limiter,避免频繁创建(rate.NewLimiter是轻量的,但 map 查找 + 初始化仍需控制) - 务必设置过期清理,否则内存无限增长(例如用
time.AfterFunc或后台 goroutine 定期清理空闲 >5 分钟的 limiter) - 不要在
Allow()前加锁 ——rate.Limiter本身是并发安全的 - 若用路径限流,注意是否带 query 参数(
/search?q=a和/search?q=b应视为同一路径还是不同?需按业务决定是否用strings.Split(c.Request.URL.Path, "?")[0]归一化)
常见错误与性能陷阱
以下问题在线上环境高频出现:
- 误用
rate.NewLimiter(rate.Limit(10), 10):第二个参数是Burst(桶容量),不是「初始令牌数」,它只影响首次突发请求能否通过 - 在中间件里调用
r.WaitN(c, n)却没设超时:会导致协程永久阻塞(应改用ctx, cancel := context.WithTimeout(c.Request.Context(), 100*time.Millisecond)再传入) - 把 limiter 存在
c.Set()里:每次请求新建,完全失去限流意义 - 用
time.Now()做 key 构造 limiter:精度太高导致几乎不会命中缓存,map 膨胀极快 - 未在响应头写入
X-RateLimit-Limit/X-RateLimit-Reset:前端或网关无法感知限流状态,排查困难
真正难的不是写限流逻辑,而是想清楚「谁该被限」「限多少」「超限时怎么反馈」—— 这些决策比代码本身影响更大。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











