iris.limiter默认依赖ctx.remoteaddr()导致反向代理下限流失效,须通过keyfunc显式提取x-forwarded-for/x-real-ip真实ip并标准化处理,生产环境必须用redis后端并合理配置连接池,超限时需自定义onlimitreached返回retry-after头与json提示。

用 iris.Limiter 做基础限流,但默认行为容易误判
Iris 自带的 iris.Limiter 中间件能按 IP 或自定义键做请求频次控制,但它默认只看 ctx.RemoteAddr(),而实际部署中常有 Nginx、CDN 或云 WAF 在前,导致所有请求的 RemoteAddr 都变成同一个(比如 127.0.0.1)。这时候限流就失效了,或者把整个入口全封掉。
解决办法是显式提取真实客户端 IP:
- 检查
X-Forwarded-For头(注意防伪造,需配合 Nginx 的set_real_ip_from配置) - 或用
ctx.GetHeader("X-Real-IP")(如果你的反向代理明确写了这个头) - 然后传给
iris.Limiter的keyFunc参数,而不是依赖默认逻辑
iris.Limiter 的 keyFunc 怎么写才安全
直接拼接字符串当 key 很危险,比如用户 ID 里含斜杠或空格,会导致 Redis key 格式错乱或被截断。正确做法是做标准化处理:
- 用
fmt.Sprintf("rate:%s:%s", cleanIP, cleanPath)构造 key,其中cleanIP是去端口、去空格、校验格式后的 IP 字符串 -
cleanPath不要用原始ctx.Path(),要先strings.TrimSuffix(ctx.Path(), "/"),避免/api/v1/draw和/api/v1/draw/被当成两个 key - 如果按用户限流,别直接用
userID,先strconv.FormatInt(userID, 10)转成纯数字字符串,防止整型溢出或类型混用
Redis 后端限流比内存限流更可靠,但要注意连接池配置
Iris 的 iris.Limiter 支持内存和 Redis 两种后端。生产环境必须用 Redis,否则多实例部署时各节点限流独立,起不到全局控制作用。但容易忽略的是 Redis 连接池设置:
- 默认
redis.Pool最大连接数是 10,抽奖类接口并发一高就卡在pool.Get()上,表现为延迟突增、503 错误 - 建议设为
MaxActive: 50,并配IdleTimeout: 60 * time.Second,避免连接堆积 - 别忘了在
limiter.NewRedisLimiter里传入你已初始化好的*redis.Pool实例,不要每次 new 一个新 pool
超限响应不能只返回 429,得带明确重试时间
默认 iris.Limiter 超限时返回空 429 响应体,前端没法知道还能等多久。你应该覆盖 OnLimitReached 回调:
- 从 limiter 内部状态读出剩余窗口时间(单位秒),用
ctx.Header("Retry-After", strconv.Itoa(remainingSeconds))写头 - 同时返回 JSON:
{"error": "rate limit exceeded", "retry_after": 60},方便前端统一拦截重试逻辑 - 注意:这个回调里不能阻塞,别在里面调 Redis 或 DB,否则限流本身成性能瓶颈
真实线上问题往往出在反向代理透传头不一致、Redis 连接池饥饿、或 key 拼接没做字符清洗——这些点比“怎么启用限流”本身重要得多。











