iris本身不内置限流中间件,需依赖第三方库如throttled/v2并手动封装为iris.handler;直接使用map+mutex手写令牌桶存在并发安全、key不可靠、无清理等问题;iris-contrib/ratelimit已归档且不支持分key限流与redis集群。

Iris 本身不内置限流中间件,得靠第三方库或手动封装。它不像 Gin 那样有成熟生态的 gin-contrib/limiter,也不像 Echo 内置了 echo/middleware.RateLimiter。直接用 Iris 的 Use 或 UseGlobal 挂一个函数做计数,容易在并发下丢精度、不支持 burst、也没存储隔离——单机还行,一上集群就失效。
用 throttled/v2 + Iris 手动包装 HTTPRateLimiter
这是目前最稳的方案,throttled 底层支持 memstore(单机)和 redis(集群),算法是 GCR A(通用信元速率),能同时控 QPS 和并发 burst。
- 别直接把
throttled.HTTPRateLimiter当中间件传给app.Use,它不是 Iris 的iris.Handler类型,得包装一层 - 关键点是把
ctx.ResponseWriter()和ctx.Request()转成标准http.ResponseWriter和*http.Request -
VaryBy字段决定 key 粒度:设Path: true就按 path 限;加IP: true就按客户端 IP + path 组合限
<pre class="brush:php;toolbar:false;">rateLimiter := throttled.HTTPRateLimiter{
RateLimiter: rateLimiterInst,
VaryBy: &throttled.VaryBy{Path: true, IP: true},
}
app.Use(func(ctx iris.Context) {
// 包装成 http.Handler 兼容接口
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 注意:必须用 ctx.Recorder().ResponseWriter() 否则写响应会失败
rw := ctx.Recorder().ResponseWriter()
rateLimiter.RateLimit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx.Next() // 放行给后续 handler
})).ServeHTTP(rw, r)
})
httpHandler.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
})
用 gorilla/secure 或自定义 middleware 做简单令牌桶
如果只是压测防护或开发环境粗略限流,不用持久化、不关心 burst,可以手写一个基于 time.Now()
- map 非并发安全,必须加
sync.RWMutex,否则高并发下 panic - key 用
ctx.RemoteAddr()不可靠(Nginx 反代后全是 127.0.0.1),建议解析X-Forwarded-For - 没自动清理机制,长期运行后 map 会无限膨胀,得配合
time.AfterFunc或后台 goroutine 定期清理过期 key
为什么别用 iris-contrib/middleware 的 ratelimit
这个包早已归档,最后更新是 2020 年,依赖的 golang.org/x/time/rate 的 Limiter 是单实例、无 key 分片能力。你没法按用户或 path 区分限流,所有请求共享同一个桶——等于全局锁,QPS 上不去,还容易误杀。
- 它的
limiter.Limit是固定值,不能动态从请求中提取 key - 没存储后端抽象,无法对接 Redis 做集群限流
- 返回状态码硬编码为 429,没法自定义响应体(比如加
Retry-Afterheader)
真正上线前,务必验证 Redis store 的连接超时和失败降级逻辑——throttled 默认遇到 Redis 错误会直接放行(fail-open),如果你要 fail-closed,得自己 wrap store 实现 fallback 行为。











