应全局复用grpc连接、用context.withtimeout控制调用超时、禁用日志中间件的请求体打印、静态文件交由系统sendfile处理。

避免在HTTP处理函数里新建gRPC连接
直接在 ctx.Handler 或控制器方法里调用 grpc.Dial 是最常见也最危险的性能陷阱。每次请求都建连,不仅耗时(DNS解析、TLS握手、TCP建连),还会快速耗尽系统文件描述符,触发 too many open files 错误。
正确做法是全局复用一个 *grpc.ClientConn 实例:
- 在应用启动时(
app.Run前)调用一次grpc.Dial,传入grpc.WithTransportCredentials和grpc.WithBlock()确保初始化成功 - 用
app.RegisterValue(&itempb.ItemServiceClient{}, client)注册生成的 client 接口,Iris 会自动注入到控制器构造函数中 - 绝不在 controller struct 字段里声明
itempb.ItemServiceClient—— Iris 不支持接口类型字段注入
用 context.WithTimeout 包裹 gRPC 调用,而非依赖 HTTP 超时
Iris 的 ctx.SetReadTimeout 或反向代理超时只控制 HTTP 层,对底层 gRPC 连接无效。若后端 gRPC 服务卡住,HTTP 请求会等满整个超时时间,线程/协程被长期占用。
必须显式构造带 deadline 的 gRPC context:
- 从
ctx.Request().Context()派生:例如ctx2, cancel := context.WithTimeout(ctx.Request().Context(), 3*time.Second) - 立即 defer
cancel(),防止 context 泄漏 - 把 metadata 注入这个新 context:
ctx2 = metadata.AppendToOutgoingContext(ctx2, "x-request-id", ctx.GetHeader("X-Request-ID")) - 用该
ctx2调用client.GetItem(ctx2, req)
关闭日志中间件的完整请求体打印
默认启用的 iris.Logger 或自定义日志中间件若开启 LogRequestBody: true,会对每个请求读取并缓存整个 body(尤其是 POST/PUT 的 JSON),造成额外内存分配和 GC 压力,QPS 下降明显。
生产环境应禁用敏感内容打印:
- 设
LogRequestBody: false和LogResponseBody: false - 如需调试,改用条件日志:仅对特定路径或错误状态码记录,例如
if statusCode >= 400才打 request ID 和 error - 避免在中间件里调用
ctx.ReadBody()或ctx.PostValues()—— 它们会提前消费 body,导致后续 handler 读不到数据
静态文件走操作系统 sendfile,别用 iris.FileServer
iris.FileServer 是纯 Go 实现,对大文件(>1MB)或高并发静态资源请求,CPU 和内存开销远高于内核级零拷贝。实测在 10K 并发下,吞吐量可差 3–5 倍。
正确姿势是交由反向代理(Nginx/Caddy)处理,或 Iris 内置优化:
- 用
app.HandleDir("/static", iris.Dir("./public"), iris.DirOptions{EnableGzip: true}),它底层调用http.ServeFile,支持sendfile系统调用(Linux)或TransmitFile(Windows) - 确保文件服务器路径不经过任何中间件(尤其不要加 JWT 验证),否则失去零拷贝优势
- 小图标、JS/CSS 文件务必开启 Gzip:
EnableGzip: true,但图片、视频等已压缩格式禁用











