限流不能靠fiber中间件统一配置,必须按下游服务粒度隔离部署,为每个第三方服务配独立rate.limiter和http.client,资源名须用“http方法:静态路径模板”格式(如get:/api/v1/users/{id}),并确保entry.exit() defer执行、sentinel.initdefault()提前调用,且限流、超时、连接池参数需数值对齐。

限流不能靠 Fiber 中间件统一配
Fiber 的中间件是路径级或全局生效的,而真实业务中不同下游服务的容量差异极大——/api/v1/payment 可能每秒只能扛 20 QPS,/api/v1/public/info 却能轻松处理 500 QPS。用一个中间件对所有路由设同一阈值,要么压垮弱服务,要么浪费强服务资源。
真正有效的限流必须按「下游服务粒度」隔离部署,比如为调用 payment-gateway 单独配一个 rate.Limiter 实例,和调用 user-profile-service 的互不干扰。
- 别在
app.Use()里写限流逻辑,那会把所有请求塞进同一个桶 - 限流入口必须落在封装好的 service 函数里,例如
paymentService.Charge()开头就 check limiter - 每个第三方服务对应一个独立的
http.Client+ 独立的rate.Limiter,避免共用连接池或令牌桶
资源名必须用静态路径模板,不能用 c.Path()
Sentinel 或自研限流器依赖稳定、可枚举的 resource name 做规则匹配。如果直接用 c.Path()(返回 /api/v1/users/123),每次 ID 不同就生成新 resource,规则永远不生效。
正确做法是结合 c.Route().Path 提取模板(Fiber v2.49+ 支持),拼成类似 POST:/api/v1/orders 或 GET:/api/v1/users/{id} 的格式:
// 推荐:从路由定义中提取静态模板
if route := c.Route(); route != nil {
resourceName := fmt.Sprintf("%s:%s", c.Method(), route.Path)
entry, err := sentinel.Entry(resourceName, sentinel.WithTrafficType(base.Inbound))
if err != nil {
return c.Status(429).SendString("rate limited")
}
defer entry.Exit()
}
- Fiber v2.48 及更早版本中
c.Route()可能返回nil,需 fallback 到预设常量(如"GET:/api/v1/users/{id}") - 务必把 HTTP 方法拼进去,
GET:/api/v1/orders和POST:/api/v1/orders必须视为两个 resource - 绝对不要用带 query 的完整 URL,
?trace_id=abc这类动态参数会让 resource 名不可控
限流失败后不能直接 panic 或忽略 error
常见错误是在 entry, err := sentinel.Entry(...) 后只判 err != nil 就返回 500,这会导致两种后果:一是没记录被限流日志,无法定位策略是否合理;二是没做 fallback,用户看到的是无意义错误而非降级内容。
- 限流失败时应明确返回
429 Too Many Requests,并附带Retry-Afterheader - 若业务允许,可在此处触发轻量 fallback,比如返回缓存数据或默认文案,但必须保证无副作用(不能写 DB、不能发消息)
- 务必确保
entry.Exit()被执行,哪怕 handler panic 了也要靠defer保证释放,否则后续请求会被误拒 - 检查
sentinel.InitDefault()是否在应用启动早期调用,否则Entry创建直接 panic
超时、连接池、限流三者必须一起配
只做限流不设超时,照样会卡死:一个慢接口耗尽所有 token 后,其他请求还在等它返回,线程池迅速堆积。必须让三者协同工作:
- 每个下游服务配专属
http.Client,显式设置Timeout、MaxIdleConns、MaxIdleConnsPerHost -
rate.Limiter的 burst 值不能大于http.Client.Transport.MaxIdleConnsPerHost,否则限流放行的并发数超过连接池上限,触发dial tcp: too many open files - 例如配置
rate.NewLimiter(rate.Every(200*time.Millisecond), 10),则MaxIdleConnsPerHost至少设为 10 - 熔断器(如
sony/gobreaker)要和限流器串行部署:先限流 → 再熔断 → 最后 fallback,顺序错乱会导致降级失效
最易被忽略的是连接池与限流速率的数值对齐——burst 是瞬时并发能力,MaxIdleConnsPerHost 是物理连接上限,两者不匹配时,限流形同虚设。











