go应用需显式调用srv.shutdown()配合带超时的context关闭http服务器,避免新连接接入和请求中断;不可用srv.close(),shutdown()返回后才可退出主goroutine。

Go 应用收到 SIGTERM 后立即停止接收新连接
默认情况下,Go 的 http.Server 在收到 SIGTERM 时不会自动关闭监听套接字,新请求仍可能被内核排队并送达,导致缩容时出现 502/503。必须显式调用 srv.Shutdown() 并配合上下文控制。
- 不要用
srv.Close():它粗暴关闭 listener,可能中断正在处理的请求 - 必须传入带超时的
context.Context,例如context.WithTimeout(context.Background(), 30*time.Second) -
Shutdown()返回后,才应退出主 goroutine;否则进程提前结束会触发强制 kill
示例关键片段:
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}()
<!-- 收到信号后 -->
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
<!-- 等待信号 -->
<!-- 收到后 -->
<h3>就绪探针必须在 Shutdown 开始时立刻返回失败</h3><p>Kubernetes 只有在 <code>readinessProbe</code> 连续失败后,才会从 Service 的 Endpoint 中移除该 Pod。如果探针还返回 200,流量就会继续打进来,而此时 <code>Shutdown()</code> 已启动,新连接被拒绝或排队失败。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件"><img
src="https://img.php.cn/upload/webcode/000/000/164/636a2b4d84031727.png" alt="使用Go语言搭建家庭相册系统-相关课件" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件" class="overflowclass">使用Go语言搭建家庭相册系统-相关课件</a>
<p class="overflowclass">使用Go语言搭建家庭相册系统-相关课件</p>
</div>
<a rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 就绪端点(如
/readyz)需维护一个原子布尔变量isReady - 收到
SIGTERM后第一件事就是设isReady = false,之后所有探针请求返回503 - 不要等
Shutdown()完成才改状态——那太晚了,Endpoint 更新有延迟 - 确保探针配置的
initialDelaySeconds足够小(如2),避免首次探测过晚
terminationGracePeriodSeconds 必须 ≥ 应用最长连接处理时间
Kubernetes 默认给 Pod 30 秒优雅终止窗口。如果 Go 应用里有长连接(如 WebSocket、gRPC 流、HTTP/2 push)、或数据库事务未提交、或下游依赖响应慢,30 秒很可能不够,导致 kubelet 强制发送 SIGKILL。
- 检查应用中最长可能的连接生命周期(例如:心跳超时 + 处理耗时),向上取整加 10 秒冗余
- 在 Deployment 的
spec.template.spec.terminationGracePeriodSeconds显式设置,比如90 - 这个值必须 ≥
Shutdown()上下文超时,否则Shutdown()还没完成,进程就被杀死了 - 注意:该字段不能设为 0(等同于强制 kill),也不能设得过大(影响缩容速度和资源释放)
StatefulSet 场景下还要处理 Headless Service 和 DNS 缓存
有状态服务常搭配 Headless Service 和 Pod 名固定(如 mysql-0.mysql)。缩容时,即使 Endpoint 已删,客户端可能仍缓存旧 DNS 记录或直连已终止 Pod 的 IP。
- 客户端必须实现重试 + 服务发现刷新(如定期
net.Resolver.LookupHost) - 避免在 Go 应用中硬编码其他 Pod 的 DNS 名;改用 Kubernetes Downward API 注入 endpoint 列表
- 若使用客户端负载均衡(如 gRPC round_robin),确保其支持健康检查回调,能感知
readinessProbe变化 - DNS 缓存 TTL 应设低(
5s),并在 CoreDNS 配置中确认未启用 aggressive caching 插件
真正卡住的点往往不在 Shutdown 本身,而在「就绪状态变更」与「Endpoint 删除」之间那几十毫秒的竞态,以及客户端是否真的信任并响应这个状态。别只测单 Pod,要压测缩容瞬间的并发请求流。










