中间件中切勿提前读取 request body,否则会消耗流导致后续 handler 无法读取;logrequestbody 等配置需设为 false,调试时仅对错误响应条件记录;body 解析应由业务 handler 自行处理。

中间件里别提前读取 request body
LogRequestBody: true 或手动调用 ctx.ReadBody()、ctx.PostValues() 会强制读取并缓存整个请求体,尤其对大 JSON 或文件上传,造成额外内存分配和 GC 压力,QPS 明显下降。
生产环境必须设 LogRequestBody: false 和 LogResponseBody: false;调试时改用条件日志,比如只在 statusCode >= 400 时记录 request ID 和错误摘要。
- 绝对不要在鉴权/日志中间件里调用
ctx.ReadBody()—— 它会消费 body,后续 handler 调用ctx.ReadJSON()就返回空或 error - 如需解析 body(比如 JWT token 在 POST body 中),应由业务 handler 自行处理,中间件只负责 header 提取和校验
- 若必须校验 body 内容(如签名验证),可用
ctx.Request().Body手动 io.Copy 到 bytes.Buffer,再 Reset 原 body,但代价高,慎用
gRPC 调用必须带 context.WithTimeout
Iris 的 HTTP 超时(如 ctx.SetReadTimeout())对底层 gRPC 连接完全无效。后端 gRPC 服务卡住时,HTTP 请求会傻等满超时时间,协程被长期占用,连接池迅速耗尽。
正确做法是每次 gRPC 调用前,从 ctx.Request().Context() 派生新 context:
- 用
ctx2, cancel := context.WithTimeout(ctx.Request().Context(), 3*time.Second),并立即defer cancel() - 把必要 metadata 注入:
ctx2 = metadata.AppendToOutgoingContext(ctx2, "x-request-id", ctx.GetHeader("X-Request-ID")) - 传给 client 方法:
client.GetItem(ctx2, req) - 别复用全局 context,也别用
context.Background()—— 会丢失请求生命周期控制
静态文件别走 iris.FileServer
iris.FileServer 是纯 Go 实现,对 >1MB 文件或高并发静态资源请求,CPU 和内存开销远高于操作系统级 sendfile 系统调用。实测在 10K QPS 下,CPU 使用率可高出 40%+。
正确方案是交由反向代理(如 Nginx)或直接用 http.FileServer 配合 http.ServeFile,让内核零拷贝处理:
- 禁用
app.HandleDir或iris.FileServer,改用 Nginx 的location /static { alias /path/to/static; } - 若必须用 Iris 托管,用
http.StripPrefix+http.FileServer(iris.Dir(...)),但不注册为 Iris 路由,避免经过中间件链 - 确保文件路径不暴露源码结构,禁止
../路径遍历 ——iris.Dir()默认已做安全过滤,但自定义 handler 仍需校验
中间件注册顺序直接影响性能热点
中间件按 Use() 调用顺序叠加执行,父分组中间件总在子分组之前运行。一个常见性能陷阱是:把耗时操作(如日志、压缩、JWT 解析)放在靠前位置,却没做短路判断。
例如鉴权中间件写成 if !valid { ctx.StatusCode(401); ctx.StopExecution() },但放在日志中间件之后,意味着每个 401 请求仍要走完日志序列化逻辑。
- 高频拦截逻辑(如限流、IP 黑名单、Basic Auth)应放在最前,尽早
ctx.StopExecution() - 耗时操作(如 JWT 解析、DB 查询)尽量延迟到真正需要时,比如放到 controller 层,而非所有请求都执行
- 用
UseGlobal()注册的中间件优先级最高,适合全局 traceID 注入,但绝不适合任何可能阻塞的操作 - 测试时用
curl -v观察X-Response-Time头,确认各中间件实际耗时分布
WithTimeout 忘记 defer cancel(),或者 gRPC client 复用但没配 grpc.WithBlock() 导致启动时连接未就绪,这些都不会报错,但会在压测时突然崩出 too many open files 或大量超时。











