iris.Limit中间件最简单但仅限IP,不支持用户级限流;默认按RemoteAddr限流,共享IP场景易误杀;限流窗口单位为秒,需手动实现分钟级逻辑;错误响应缺Retry-After头,需自定义补充。

用 iris.Limit 中间件最简单,但要注意它只限 IP 不限用户
直接在路由上加 iris.Limit 是最快的方式,它基于内存计数,开箱即用。不过默认行为是按客户端 RemoteAddr(通常是 IP)限流,不是按登录用户或 token,所以共享出口 IP 的用户会被误杀,比如公司内网、校园网、某些代理后的用户。
实操建议:
- 适合内部系统、后台管理接口或对精度要求不高的场景
- 限流窗口单位是秒,不能设成分钟或小时,比如
iris.Limit(10, 60)实际是「每秒最多 10 次」,不是「每分钟最多 10 次」——这是常见误解 - 如果想按分钟限流,得自己算:比如「每分钟 60 次」要写成
iris.Limit(1, 1)(每秒 1 次),再配合滑动窗口逻辑,iris.Limit本身不支持 - 错误响应默认是 429,但不带
Retry-After头,前端没法知道等多久,需要手动补
自定义中间件 + Redis 实现精准用户级限流
要按 Authorization token、X-User-ID 或 session ID 限流,必须绕过 iris.Limit,自己写中间件 + 外部存储。Redis 是首选,因为原子操作(INCR + EXPIRE)能保证并发安全。
关键点:
- 用
redis.Incr增加计数,再用redis.Expire设置过期时间,两步必须用 Lua 脚本合并执行,否则竞态条件会导致超限 - 不要在中间件里直接调
c.StopExecution(),而应统一返回 429 并写入Retry-After,比如剩余等待秒数 - 注意 Redis 连接池配置,高并发下连接耗尽会拖慢整个服务;Iris 默认不带 Redis 客户端,得自己集成
github.com/go-redis/redis/v9 - 示例片段核心逻辑:
key := fmt.Sprintf("rate:uid:%s:%s", userID, time.Now().UTC().Truncate(time.Minute))<br>val, err := rdb.Incr(ctx, key).Result()<br>if err == nil && val == 1 {<br> rdb.Expire(ctx, key, time.Minute)<br>}
iris.Limit 和自定义限流的性能与兼容性差异
内存限流快但不可靠,Redis 限流稳但有网络延迟和失败风险。实际压测中,纯内存方案 QPS 能到 3w+,而 Redis 方案在本地 Redis 下约 8k–12k,走外网 Redis 可能掉到 2k 以下。
选型要看场景:
- API 网关层建议用独立限流服务(如 Kong、Sentinel),别塞进 Iris 应用里
- Iris 应用本身做二级限流(比如先内存粗筛,再 Redis 精筛)可以平衡性能和精度
- 注意 Iris v12+ 的中间件执行顺序:限流中间件必须放在
auth之后,否则拿不到用户标识 - 如果用了
iris.Compression,确保限流逻辑在压缩之前运行,否则可能因响应体被改写而干扰判断
容易被忽略的边界问题
限流不是加个中间件就完事,很多坑藏在细节里:
- IPv6 地址格式不同,
c.RemoteAddr()可能带端口或方括号,提取 IP 要用net.ParseIP+strings.Split清洗,否则同一用户被当成多个 IP - 前端发预检请求(
OPTIONS)也会触发限流,应该在中间件里跳过非业务方法 - 测试时用
curl -H "X-Forwarded-For: 1.2.3.4"模拟真实 IP,但 Iris 默认不信任该头,需显式配置app.SetFirewall(iris.Firewall{TrustProxy: true}) - 没有降级策略:Redis 挂了,限流中间件抛错会导致整个接口不可用,至少得加
defer捕获并记录 warn 日志,继续放行
真正难的不是写限流代码,而是确定“谁该被限”“限多少”“怎么让前端友好重试”,这些都得结合业务日志反复调。











