Go长连接网关弹性伸缩需就绪探针、优雅关闭与注册中心协同:仅用http.Server.Shutdown无法阻止上游继续转发新请求,K8s Endpoint更新有延迟,WebSocket/SSE需手动管理连接生命周期,PreStop须配合deregister耗时与最大连接存活时间设置sleep,terminationGracePeriodSeconds必须覆盖全过程。

Go长连接网关在弹性伸缩场景下,必须配合就绪探针(readinessProbe)、优雅关闭(http.Server.Shutdown)和注册中心生命周期协同,才能真正实现连接不中断、请求不丢、新老流量分流——只做其中一环,大概率仍有 connection reset 或 broken pipe 报错。
为什么 http.Server.Shutdown 单独用不顶事
很多团队以为调用 srv.Shutdown(ctx) 就算“优雅”了,但实际线上仍出现长连接断开、SSE流中断、WebSocket握手失败。根本原因是:它只管 Go 服务端的监听器和连接池,不管上游负载均衡或注册中心是否还在转发新请求。
-
Shutdown不会主动通知注册中心下线,Kubernetes 的Endpoint更新有延迟(默认 1–3 秒),期间新请求仍会被打到正在关闭的 Pod - 若使用 Consul/Eureka/Nacos,服务注销动作需显式触发,且要等 deregister 完成后再执行
Shutdown,否则连接还没处理完,实例就被摘除 - 对于 WebSocket/SSE,客户端可能重连频繁,而
Shutdown的超时时间(如 30s)若短于客户端重连间隔,就会出现“刚关完又连上”的假象,实则已丢包
就绪探针必须区分「启动中」和「可下线」两个状态
Kubernetes 默认只用一个 readinessProbe 控制流量进入,但长连接网关需要两个独立信号:一个是“能收新请求”,另一个是“不再收新请求,但老连接继续服务”。否则滚动更新时,新 Pod 还没注册好,老 Pod 就被切走流量。
- 建议暴露两个 HTTP 端点:
/healthz/ready(判断是否完成注册、加载路由、连接后端成功),/healthz/shutting-down(由 PreStop 触发,返回 503 表示拒绝新连接,但保持旧连接) -
readinessProbe指向/healthz/ready;PreStop 中 curlhttp://localhost:8080/healthz/shutting-down,再 sleep 2s,确保 Endpoint 同步完成 - 避免把
livenessProbe和readinessProbe指向同一路径——健康检查误判会导致重启循环,尤其在高内存压力下
连接级优雅关闭必须按协议分层处理
Go 的 http.Server 对 HTTP/1.1 长连接支持较好,但对 WebSocket 和 SSE 流式响应,需手动管理连接生命周期,否则 Shutdown 会直接关闭底层 net.Conn,触发 RST。
- WebSocket 场景:用
gorilla/websocket时,需在Shutdown前遍历所有活跃连接,调用conn.WriteMessage(websocket.CloseMessage, ...)主动发 close 帧,再等conn.ReadMessage返回io.EOF或超时后才关闭 - SSE 场景:不能简单用
http.ResponseWriter写入,要搭配context.WithTimeout控制单次响应生命周期,并在Shutdown前向每个响应 writer 发送event: close\n\n,再 flush - 务必设置
http.Server.IdleTimeout和ReadTimeout,防止慢连接长期占着 goroutine 不释放
注册中心与网关联动的关键时间窗口
从 PreStop 开始,到所有长连接自然结束,中间存在一个必须被覆盖的“安全窗口”。这个窗口不是固定值,取决于业务最长连接持续时间(比如游戏心跳周期、AI 推理 SSE 流最大耗时)。
- PreStop 中 sleep 时间 ≥ 注册中心 deregister 耗时 + 最大连接预期存活时间(例如:Nacos deregister 平均 800ms + 游戏客户端心跳 30s = 至少 31s)
- Pod 的
terminationGracePeriodSeconds必须 ≥ PreStop 总耗时,否则 kubelet 强杀进程,Shutdown根本没机会执行 - 云原生网关(如 MSE/ASM)若作为前置 LB,需确认其是否支持 Envoy 的
drain_connections行为;不支持的话,得靠网关自身实现连接摘流逻辑,而非依赖 Kubernetes
最容易被忽略的是:长连接的“无损”不等于“零感知”。客户端仍可能收到一次 503 或重连延迟,关键在于服务端不能丢数据、不能中断正在进行的流。真正的平滑,是让连接自然走到终点,而不是强行掐断再重连。


















