全局限流中间件先执行,路由级限流后执行;执行顺序由注册时机决定,全局限流挂载在所有路由链最外层,形成“全局限流 → 路由级限流 → handler”的洋葱模型。

全局限流中间件和路由级限流同时存在时,谁先执行?
执行顺序由注册时机决定,不是“谁更优先”,而是“谁先挂载谁先跑”。r.Use()注册的全局限流中间件会插入到所有后续注册路由的中间件链最外层(洋葱模型最外圈),而路由级限流(如r.GET("/api/pay", payRateLimit, payHandler))则被塞进该路由专属 handler 链的内层。结果是:全局限流 → 路由级限流 → handler。
这意味着如果两个限流器都触发拦截,用户看到的是全局限流返回的 429,根本不会走到路由级限流那一步——除非你显式跳过全局的那一个。
- 全局限流对所有路径生效,包括健康检查、静态资源、登录接口,容易误伤
- 路由级限流只作用于明确指定的那一行,比如
r.POST("/order", orderLimiter, createOrder) - 若两者阈值不同(如全局 100rps、单路由 5rps),实际生效的是更严的那个,但错误响应来源不可控
如何让某个路由彻底绕过全局限流?
不能靠“后注册覆盖”,Gin 没有中间件卸载机制。必须在全局限流中间件内部做路径判断,主动跳过。
关键不是“排除”,而是“匹配后直接放行”,避免调用 c.Next() 进入后续链:
func GlobalRateLimit() gin.HandlerFunc {
limiter := NewLimiter(100, 100) // 全局 100rps
return func(c *gin.Context) {
// 明确放行的路径,不参与限流
if c.Request.URL.Path == "/health" ||
c.Request.URL.Path == "/login" ||
strings.HasPrefix(c.Request.URL.Path, "/static/") {
c.Next()
return
}
if !limiter.Allow() {
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "global rate limit exceeded"})
return
}
c.Next()
}
}
- 路径判断必须用
c.Request.URL.Path,不是c.FullPath()(后者含通配符,不可靠) - 不要用
strings.Contains()做模糊匹配,/user 和 /users 会被同时误杀 - 放行逻辑必须放在限流判断之前,否则
Allow()已经消耗一次令牌
为什么把限流写成路由级中间件反而更可控?
因为它的作用域干净、意图明确,且天然规避了“全局 vs 局部”的执行冲突。你不需要在全局中间件里写一堆 if 判断,也不用担心某天加了个新路由忘了加白名单。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
典型适用场景:
- 支付回调接口(
/webhook/alipay)需要单独设为 1rps,其他接口走全局 50rps - 搜索接口(
/search)允许突发流量,但用户资料接口(/user/profile)必须严格限流 - 灰度发布时,只对
/api/v2/下的路由启用新限流策略,旧版不受影响
写法就是最朴素的:r.POST("/webhook/alipay", alipayWebhookLimiter, handleAlipay)。中间件函数本身和全局版完全一样,只是传入位置变了。
最容易被忽略的坑:限流中间件里的并发安全
所有自实现的限流器(比如基于 tokens int 字段的令牌桶)在多协程下都是非线程安全的。Gin 每个请求都在独立 goroutine 中运行,Allow() 方法若没加锁或没用原子操作,会出现令牌数错乱、漏放行或误拦截。
别自己手写带状态的限流器,除非你真用了 sync.Mutex 或 atomic 包。更稳妥的做法:
- 用
go.uber.org/ratelimit(漏桶,无状态,线程安全) - 用
golang.org/x/time/rate(令牌桶,rate.Limiter是并发安全的) - 把限流状态下沉到 Redis(如
redis-cell模块),由服务端统一裁决
本地内存限流只适合开发或低流量场景;上线前务必确认你的限流器在压测中不会因竞态导致阈值漂移。










