ratelimit默认不可靠,因内存store+空key导致全局限流;需用redis store和ip/token等稳定keyfunc实现精准限流,并手动终止流程防handler继续执行。

直接用 middleware.RateLimit 最快,但默认配置在高并发下会误杀;必须手动配 Store 和 KeyFunc 才能按 IP 或用户 ID 精确限流。
RateLimit 中间件默认行为为什么不可靠
开箱即用的 middleware.RateLimit 默认使用内存存储(memory.NewStore()),且 KeyFunc 固定返回空字符串——这意味着所有请求共用同一个计数器。压测时哪怕只有一条路径被刷,整个服务的其他接口也会被连带限流。
- 现象:访问
/api/v1/users被限,但/health也返回 429 - 根本原因:没指定
KeyFunc,所有请求 key 都是"",计数器无法区分维度 - 内存 Store 在多实例部署下完全失效,各节点各自计数,限流形同虚设
如何正确配置 RateLimit 实现按 IP 限流
核心是替换默认 Store 并定义可区分的限流 key。推荐用 redis.NewStore + 自定义 KeyFunc:
- 安装依赖:
go get github.com/gofiber/fiber/v2/middleware/limiter和github.com/gofiber/fiber/v2/middleware/limiter/store/redis -
KeyFunc应返回稳定标识,例如:c.IP()(注意:NAT 环境下需从X-Forwarded-For提取) - Redis Store 初始化时必须传入
*redis.Client,不能只传地址字符串 - 示例片段:
app.Use(limiter.New(limiter.Config{ Max: 100, Period: 30 * time.Second, KeyFunc: func(c *fiber.Ctx) string { return c.IP() }, Store: redis.NewStore(client), // client 是 *redis.Client }))
按用户 ID 或 API Key 限流的关键细节
如果要按业务维度(如 Authorization header 中的 token)限流,KeyFunc 必须能安全提取并归一化值,否则会因大小写、前缀差异导致同一用户被算作多个 key。
- 先校验 header 是否存在,避免 panic:
auth := c.Get("Authorization"),再检查strings.HasPrefix(auth, "Bearer ") - 提取 token 后建议做
strings.TrimSpace和小写哈希(如fmt.Sprintf("%x", md5.Sum([]byte(token)))),防止 token 携带空格或大小写扰动 - 不要在
KeyFunc里做 DB 查询或远程调用——它会在每次请求都执行,是性能瓶颈点 - 若 token 解析失败,应 fallback 到
c.IP(),而不是返回空字符串,否则又回到全局共享计数器
RateLimit 触发后为什么 handler 还在执行
middleware.RateLimit 是前置拦截,触发 429 后会调用 c.Next() 继续走后续中间件和 handler——这明显违背预期。必须显式终止流程。
- 正确做法:在配置中设置
MiddlewareFunc,里面手动调用c.Status(429).SendString("Too many requests")后立即return - 切勿依赖
Next()的隐式跳过逻辑;Fiber 不会自动 abort 后续链 - 如果你用了自定义错误处理中间件,要确保它不覆盖 429 响应体——
RateLimit的响应是原始字符串,不是 JSON
真正难的不是加一行 app.Use(limiter.New(...)),而是确认 KeyFunc 返回的每个 key 都能稳定映射到真实业务主体,并且 Store 能跨进程一致计数。Redis 是底线,内存 Store 只适合单机调试。











