因为Fiber默认不带限流中间件,fiber.New()创建的实例不处理频率控制,必须显式注册fiber.Limiter并正确配置Max、LimitBy、Expiration参数,且全局挂载在Use()中,否则限流完全不生效。

为什么直接用 fiber.New() 无法限流
因为 Fiber 默认不带限流中间件,fiber.New() 创建的 app 实例本身不处理请求频率控制。你得显式注册限流逻辑,否则所有请求畅通无阻,哪怕加了 fiber.Limiter 相关配置也不会生效。
常见错误是只写了 app.Use(fiber.Limiter(...)) 却没注意参数构造是否合法,或者把限流中间件挂载在了错误路由层级(比如只对某个子路由生效,而漏掉了 API 入口根路径)。
- 限流中间件必须在
app.Use()中全局注册,或至少覆盖目标路由组 -
fiber.Limiter是 Fiber v2.40+ 内置的中间件,旧版本需自行集成github.com/ulule/limiter - 默认内存存储(
memory.NewStore)不适用于多实例部署,会各自计数失效
fiber.Limiter 的三个关键参数怎么设
最常配错的是 max、limitBy 和 expiration。它们共同决定“谁被限”“限多少”“多久重置”。
例如想限制每个 IP 每分钟最多 100 次请求,应该这样写:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
app.Use(fiber.Limiter{
Max: 100,
LimitBy: fiber.LimitByIP,
Expiration: 1 * time.Minute,
})-
Max是整数,不是字符串;设为0表示不限制(但别这么写,容易误判) -
LimitBy可选fiber.LimitByIP、fiber.LimitByHeader(如传X-User-ID)、fiber.LimitByQuery(如?token=xxx) -
Expiration必须是time.Duration,写60 * time.Second和1 * time.Minute等价,但写60就是 60 纳秒,直接崩
如何让限流支持 Redis 后端
单机内存限流在负载均衡或多进程场景下完全不可靠,必须换存储。Fiber 官方推荐用 github.com/gofiber/fiber/v2/middleware/limiter 配合 github.com/ulule/limiter/v3 的 Redis 驱动。
步骤很简单:引入驱动、构造 store、传给 fiber.Limiter:
import (
"github.com/ulule/limiter/v3"
"github.com/ulule/limiter/v3/drivers/store/redis"
"github.com/go-redis/redis/v8"
)
<p>rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
store, err := redis.NewStore(rdb)
if err != nil {
log.Fatal(err)
}</p><p>app.Use(fiber.Limiter{
Max: 100,
LimitBy: fiber.LimitByIP,
Expiration: 1 * time.Minute,
Store: store,
})- 注意
redis.NewClient返回的是*redis.Client,不是redis.Conn或其他封装 - 如果 Redis 连接失败,
limiter不会 panic,但会退化为内存模式——这点极难察觉,建议启动时主动store.Get(...)测通 - Redis key 命名默认含前缀
limiter:,可通过redis.NewStoreWithOptions自定义
限流触发后怎么返回友好提示
默认返回状态码 429 Too Many Requests 和空响应体,前端很难区分是限流还是服务异常。你应该自定义 Message 和 Handler。
比如返回 JSON 提示剩余等待秒数:
app.Use(fiber.Limiter{
Max: 100,
LimitBy: fiber.LimitByIP,
Expiration: 1 * time.Minute,
Message: `{"error": "rate limited", "retry_after": {{.RetryAfter}}}`,
Handler: func(c *fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).SendString(c.Locals("message").(string))
},
})-
{{.RetryAfter}}是模板变量,会被自动替换为整数秒(不是时间戳) -
c.Locals("message")是中间件内部注入的渲染后字符串,别试图从c.Response().StatusCode()推断 - 如果用了自定义
Handler,就别再依赖Message字段做格式化,二者选其一即可
真正麻烦的是分布式环境下 RetryAfter 的精度问题:Redis 里过期时间和本地时钟不同步时,这个值可能不准。生产环境建议前端以服务端返回为准,不做本地倒计时补偿。

















