iris 未内置 onshutdown 钩子,需手动构造 http.server 并监听 sigint/sigterm 信号,在收到信号后调用 srv.shutdown() 执行优雅关机与资源清理。

Go 进程退出时 Iris 没提供 OnShutdown 钩子
Iris 本身不暴露标准的 http.Server shutdown hook,也没有内置类似 app.OnShutdown(func()) 的注册机制。它把服务生命周期控制权交给了底层 http.Server,而你自己得手动接管——否则 SIGINT(Ctrl+C)、kill -15 等信号到来时,连接可能被粗暴中断,数据库连接没关闭、日志没刷盘、长任务被砍断。
必须用 iris.Server() 包裹自定义 http.Server 实例
想监听关闭事件,就不能只写 app.Run(iris.Addr(":8080"))。得自己构造 *http.Server,并在调用 app.Run() 前注册 Shutdown 逻辑:
<pre class="brush:php;toolbar:false;">srv := &http.Server{
Addr: ":8080",
Handler: app,
}
// 启动前注册 shutdown 处理
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
app.Logger().Fatal(err)
}
}()
// 捕获 OS 信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<strong><font color="red">// 关键:等信号后主动调 Shutdown()</font></strong>
<code><strong><font color="red"><code>sig</code> := </font></strong></code><code><strong><font color="red"><code></code></font></strong></code>
app.Logger().Infof("Shutting down server... (signal: %v)", sig)
// 此处可加清理逻辑:DB.Close(), cache.Flush(), etc.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
app.Logger().Errorf("Server shutdown error: %v", err)
}
中间件或路由里不能依赖 defer
做清理
很多人在 handler 里写 defer db.Close() 或 defer log.Flush(),但这只对单次请求生效。服务器整体关闭时,这些 defer 不会触发。真正要管的是进程级资源释放:
-
app.Run()返回后才执行的代码,无法捕获优雅关机过程(Iris v12+ 的Run()是阻塞调用) - 所有全局资源(如数据库连接池、Redis 客户端、文件句柄)必须在
srv.Shutdown()调用前显式关闭 - 若用了第三方库(如 GORM、ent),检查其是否支持
WithContext(ctx)传入超时上下文,避免Shutdown卡住
别漏掉 context.WithTimeout 的超时时间设置
srv.Shutdown() 会等待正在处理的请求结束,但不会无限等。超时时间太短,活跃连接被强制断开;太长,运维等待时间不可控:
- 生产环境建议设为 5–15 秒,取决于业务平均响应时长
- 若存在长轮询或 WebSocket 连接,需额外处理连接迁移或心跳超时逻辑
- 不要用
context.Background()—— 它没有截止时间,Shutdown()可能永远不返回
http.Server 拿出来、挂上信号监听、再喂给 app.Run()。最容易忽略的点是:**以为 app.Run() 返回就代表服务停了,其实它返回时 shutdown 可能还没开始**——真正的清理入口,只在你自己写的 srv.Shutdown() 调用之后。











