fiber框架默认不内置限流中间件,因遵循“只做核心路由”设计哲学,需手动集成如fiber-rate-limiter或redis+lua方案;禁用共享变量计数与sleep阻塞式限流。

Fiber 框架本身不内置限流中间件,必须手动集成第三方限流器或自建逻辑;直接用 rate-limiter 或 redis-rate-limiter 是最稳妥的选择。
为什么 Fiber 默认没有限流能力
Fiber 是 Go 语言的轻量级 Web 框架,设计哲学是“只做核心路由和中间件基础”,所有非 HTTP 协议层功能(如限流、熔断、认证)都交由社区中间件实现。它不像 SpringBoot 那样有生态绑定的限流组件,也不像 Express.js 那样默认带 express-rate-limit。这意味着:你得自己选、自己装、自己配。
常见误区是试图用 fiber.App.Use() 直接套一个计数器闭包——这在多协程并发下会因共享变量竞争而失效,sync.Mutex 又扛不住高并发,必须依赖原子操作或外部存储。
用 fiber-rate-limiter 实现 IP 级限流
这是目前最成熟、维护活跃的 Fiber 限流中间件(v2.4+ 支持 Redis 后端)。它基于令牌桶算法,支持时间窗口、突发容量、自定义 key 生成等关键能力。
- 安装:
go get github.com/gofiber/fiber/v2/middleware/limiter - 基础用法(每分钟最多 100 次请求):
app.Use(limiter.New(limiter.Config{ Max: 100, Expires: 1 * time.Minute, KeyGenerator: func(c *fiber.Ctx) string { return c.IP() // 基于客户端 IP }, })) - 注意
KeyGenerator返回值不能含非法字符(如冒号、空格),否则 Redis 存储失败;建议用strings.ReplaceAll(c.IP(), ":", "-")预处理 - 错误响应默认是 429,但不会自动写 body;需配合
Next或自定义Handler补充提示信息
Redis + Lua 脚本实现分布式精准限流
当需要按用户 ID、API 路径、商品 ID 等维度限流时,单机内存方案不可靠,必须上 Redis。Fiber 本身不绑 Redis 客户端,推荐用 github.com/redis/go-redis/v9 配合 Lua 脚本保证原子性。
关键点在于:不要用 INCR + EXPIRE 两步操作(存在竞态),而要用一段 Lua 脚本一次性完成「读、判、增、设过期」:
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call("INCR", key)
if current == 1 then
redis.call("EXPIRE", key, window)
end
return current > limit
在 Fiber 中调用示例:
redisClient.Eval(ctx, luaScript, []string{rateKey}, maxCount, windowSec)返回 true 表示触发限流,此时应中断请求并返回 429。容易忽略的坑:rateKey 必须唯一且可预测(比如 "rl:user:" + c.Params("id") + ":/api/order"),否则不同路径或参数会被误判为同一限流桶;同时注意 Redis 连接池大小,QPS 超过 5000 时连接池不足会导致超时。
避免用 time.Sleep 或 sync.WaitGroup 做限流
有些开发者尝试在中间件里对超限请求调用 time.Sleep 来“延迟放行”,这本质是漏桶的劣化实现,但会严重拖慢整个 goroutine,导致连接堆积、FD 耗尽;更糟的是用 sync.WaitGroup 等待令牌释放——它无法跨 goroutine 通知,且阻塞期间无法响应取消信号(ctx.Done())。
真正可行的只有两类方案:
① 拒绝式:立刻返回 429,不占资源
② 异步排队式:把请求推入消息队列(如 Kafka/RabbitMQ),由后台 worker 消费——但这已超出限流范畴,属于削峰填谷架构设计。
最后提醒:Fiber 的 c.Next() 不代表“继续执行后续中间件”,而是“进入路由匹配流程”,一旦你在限流中间件里没显式 return,即使触发了限流逻辑,业务 handler 仍可能被执行。务必在判断超限后立即 return。











