用 fiber.Limiter 可5行代码实现基于令牌桶的单机限流,默认按IP、支持自定义KeyGenerator(如解析JWT用户ID)、生产需切换Redis存储并配置连接复用与降级,同时手动添加剩余次数和重试时间响应头。

用 fiber.Limiter 快速加基础限流
直接用 Fiber 内置的 fiber.Limiter 中间件,5 行代码就能限制每分钟请求次数。它底层基于令牌桶(token bucket),默认使用内存存储,适合单机部署。
常见错误是没传 max 或漏掉 limit.Next 的调用,导致限流不生效或 panic。
-
max是每单位时间允许的最大请求数,比如100表示每分钟最多 100 次 -
limit.Next必须在中间件里显式调用,否则不会触发计数逻辑 - 单位时间由
expiration控制,默认是time.Minute,别写成字符串
app.Use(fiber.Limiter{
Max: 100,
Expiration: 1 * time.Minute,
})
按 IP 或用户 ID 区分限流策略
默认 fiber.Limiter 按客户端 IP 限流,但实际中常需按 Authorization 头里的 token 解析出用户 ID 来限流。这时得自定义 KeyGenerator 函数。
容易踩的坑:从 header 取 token 后没做空值判断,或者 JWT 解析失败时返回空 key,导致所有请求被归到同一个桶里,集体被限。
- key 必须唯一且稳定,建议用
user_id而非原始 token 字符串 - 如果解析失败,应返回一个兜底 key(如
"unknown"),避免 key 为空引发 panic - 注意 JWT 验证必须同步完成,不能在 goroutine 里异步解析
app.Use(fiber.Limiter{
Max: 50,
KeyGenerator: func(c *fiber.Ctx) string {
auth := c.Get("Authorization")
if auth == "" {
return "ip:" + c.IP()
}
userID, err := parseUserIDFromToken(auth) // 你自己的解析函数
if err != nil {
return "invalid_token"
}
return "user:" + userID
},
})
生产环境必须换 Redis 存储
单机内存限流在多实例部署下完全失效——每个进程维护自己的桶,总流量会翻倍突破限制。Fiber 支持用 redis 作为存储后端,靠原子命令保证一致性。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
关键点不在“怎么连 Redis”,而在于连接复用和错误降级。直接 new redis client 每次都建连接,QPS 上去就卡死;更糟的是 Redis 不可用时没 fallback,整个 API 直接 500。
- 用全局复用的
*redis.Client,别在中间件里临时 New - 设置
OnLimitReached回调,记录日志并返回友好错误(如429 Too Many Requests) - 务必加
ctx.Timeout,防止 Redis 延迟拖垮整个请求链路
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
<p>app.Use(fiber.Limiter{
Max: 200,
Store: fiber.NewRedisStore(redisClient),
OnLimitReached: func(c *fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{"error": "rate limit exceeded"})
},
})
</p>
限流后返回剩余次数和重试时间
前端需要知道还能发几次、多久后恢复,否则只能盲等。Fiber 默认不写响应头,得手动加 X-RateLimit-Remaining 和 X-RateLimit-Reset。
难点在于:Redis 存储时无法直接拿到当前剩余数和重置时间戳,得用 store.Get 主动查一次,但这个操作本身有延迟和失败可能,不能阻塞主流程。
- 推荐在
OnLimitReached里补全头信息,只对被拒绝的请求写,减少开销 - 剩余数可从
c.Locals("limiter_remaining")获取(需启用Next的 context 注入) - 重置时间戳用
time.Now().Add(expiration).Unix()算,别依赖 Redis 返回的 TTL,精度低
真正复杂的地方是:当你的服务横跨多个时区、又用了 NTP 不稳的容器节点,Unix() 时间戳可能漂移。上线前一定用真实压测验证重试窗口是否准确。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










