服务下线需确保连接自然断开而非强制中断:通过监控ESTABLISHED连接下降、反向代理摘流、应用监听SIGTERM并优雅关闭、systemd配置TimeoutStopSec及健康检查、LB配合剔除流量,并验证端口释放、无活跃连接、注册状态清除。

服务下线前如何确认连接已自然断开
直接 systemctl stop 或杀进程会导致活跃 TCP 连接被 RST 中断,客户端可能收到 “Connection reset by peer” 或超时失败。关键不是“停得快”,而是“等得清”。
- 用
ss -tuln state established | grep :或netstat -tnp | grep :观察当前 ESTABLISHED 连接数是否持续下降 - 对 HTTP 服务,可临时在反向代理(如 Nginx)中设置
proxy_next_upstream error timeout http_502并将该节点权重设为 0,让流量逐步摘除 - 应用层需监听
SIGTERM,收到后关闭新连接入口(如 HTTP Server 的srv.Close()),但保持旧连接直到自然结束或超时
如何用 systemd 实现可控的优雅终止窗口
默认 systemctl stop 只给 90 秒,且不等待应用真正释放端口和连接。必须显式配置超时与终止逻辑。
- 在 service 文件中设置:
TimeoutStopSec=300(延长终止等待时间)ExecStop=/bin/sh -c 'kill -TERM $MAINPID && sleep 2 && kill -CONT $MAINPID 2>/dev/null || true'(仅作示意,实际应由应用自身处理) - 更可靠的做法是:应用内实现
shutdown流程,并在ExecStop中调用其健康检查接口(如curl -f http://localhost:8080/healthz?ready=false)再等待连接归零 - 避免使用
KillMode=none,它会让 systemd 放弃管理子进程,导致残留 worker 进程无法被回收
负载均衡器侧如何配合完成平滑摘流
服务端停了,但 LB 还在转发请求,就会产生 502/503。LB 不是旁观者,而是平滑下线的关键一环。
- Nginx:先执行
upstream_hash或ip_hash配合down标记,再 reload;或用max_fails=1 fail_timeout=10s让健康检查快速剔除 - Consul:调用
PUT /v1/agent/service/deregister/,Consul 会先触发 TTL 失效、再通知所有 client 刷新本地缓存 - Kubernetes:修改 readiness probe 返回失败,再
kubectl scale deploy/;确保--replicas=0 terminationGracePeriodSeconds≥ 应用最大连接超时(如 300s)
验证下线是否真正完成的三个必查点
很多人以为 systemctl status 显示 inactive 就完事了,其实端口、连接、注册状态可能还挂着。
- 查端口:
lsof -i :或ss -tlnp | grep :—— 必须无输出 - 查连接:
ss -tan state fin-wait-1,state fin-wait-2,state time-wait | grep :—— TIME-WAIT 是正常残留,但不应有 ESTABLISHED 或 CLOSE-WAIT - 查注册中心:
curl http://consul:8500/v1/health/service/或dig @—— 确认服务条目已消失或状态为 critical.service.consul
TIME-WAIT 状态本身不可怕,但若持续存在大量未回收连接,说明应用没正确调用 Close() 或设置了过短的 SO_LINGER,这会在高频下线场景中暴露资源泄漏问题。










