iris 默认不阻塞新请求是因为其 run() 底层调用 http.server.serve(),而 shutdown() 仅停止接受新连接,不拦截已转发但未到达的请求;需结合 prestop、readinessprobe 和超时可控的 shutdown() 实现优雅下线。

为什么 Iris 默认不阻塞新请求进入
Iris 的 iris.Run() 底层调用的是标准库 http.Server.Serve(),而 Go 标准库的 http.Server.Shutdown() 本身只负责「停止接受新连接」,但不会主动拒绝已建立 TCP 连接上正在抵达的 HTTP 请求。这意味着:SIGTERM 发出后,负载均衡器(如 Nginx、Istio Ingress)若未及时摘除节点,新请求仍可能被转发进来,而此时 Iris 已在关闭监听套接字,这些请求会直接返回 502 或超时——这不是 Iris 的 bug,是网络层与应用层协同缺失的典型表现。
preStop + Shutdown() 配合才是关键路径
在 Kubernetes 环境中,必须靠 preStop 钩子争取缓冲时间,让上游流量下线,再触发应用层 Shutdown。Iris 本身不封装容器生命周期逻辑,你需要手动组合:
- 在
preStop中先通知 LB 摘流(比如调用就绪探针接口返回失败,或 sleep 等待 Endpoint 被移除) - 同时在应用内监听
SIGTERM,调用app.Shutdown()(注意不是app.Close()) -
app.Shutdown()会调用底层http.Server.Shutdown(),并等待所有活跃请求完成(默认无超时,需自行控制)
示例片段:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
srv := &http.Server{Addr: ":8080", Handler: app}
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}()
// 收到 SIGTERM 后开始优雅停机
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
<h3>
<a style="color:#f60; text-decoration:underline;" title="路由" href="https://m.php.cn/zt/18172.html" target="_blank">路由</a>保护要靠外部机制兜底</h3>
<p>Iris 本身没有「停机中自动返回 503」这类内置路由拦截开关。所谓“路由保护”,实际是让请求在到达 Iris 路由前就被拦截。可行方案包括:</p>
- Kubernetes 中配合
readinessProbe:停机流程一开始就把探针设为失败,K8s 自动从 Endpoints 移除该 Pod - 在反向代理层(如 Nginx)配置
proxy_next_upstream error timeout http_503,并确保 preStop 中有足够延迟(如sleep 5)让 upstream 刷新 - 若必须在 Iris 内部做,可手动加一个全局中间件,在收到停机信号后返回 503,但要注意:这个中间件只能拦住新进来的请求,无法阻止已在处理中的长连接或慢请求
容易被忽略的超时陷阱
http.Server.Shutdown() 的 context 超时不是“最多等多久”,而是“最多等多久后强制终止”。如果设得太短(如 5s),大量正在 DB 查询或远程调用的请求会被粗暴中断,导致数据不一致;设得太长(如 120s),又可能拖慢整个滚动更新节奏。生产环境建议:
- 根据业务最长耗时链路定上限(比如支付回调平均耗时 8s,那就设 15s)
- 在 Shutdown 前主动关闭数据库连接池、gRPC 客户端等依赖资源,避免它们成为 Shutdown 卡点
- 不要依赖 Iris 的
app.Close(),它不等待请求,仅释放内部状态,等同于粗暴 kill
真正的优雅,从来不在框架里,而在你对信号、超时、依赖和基础设施边界的清晰认知里。










