功能说明
示例演示负载均衡后面的优雅下线:收到终止信号后先把 /readyz 置为 503,让负载均衡停止把新流量路由进来;等待一段传播时间,再用独立的超时上下文调用 server.Shutdown,等待在途请求完成。这样既不会切断正在处理的请求,也不会在探针仍显示就绪时突然关闭监听。
关键代码
<-ctx.Done()
ready.Store(false)
fmt.Println("readiness probe now fails; waiting for load balancers to stop routing")
time.Sleep(100 * time.Millisecond)
shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
fmt.Println("shutdown:", err)
return
}
ready 用 atomic.Bool 保存,处理探针的 goroutine 与主协程并发读写不需要额外加锁。
使用注意
- 探针传播时间取决于负载均衡配置,示例的 100 毫秒只是演示值,生产环境应大于健康检查周期。
- 在等待传播期间服务仍然接受请求,只是探针返回失败;这依赖负载均衡真正尊重 readiness。
Shutdown不会取消正在执行的请求上下文,慢处理器需要自己监听r.Context()。- 必须给
Shutdown单独设置超时,否则异常请求可能让进程一直不退出。 - 容器编排的宽限期应覆盖“传播等待 + 排空时间”,否则进程会被 SIGKILL。
验证方式
运行示例,客户端应收到 200 和 finished,随后依次打印探针失败、等待路由撤销、服务排空完成。可以把传播等待改为 0,观察探针与关闭几乎同时发生;把处理时间改成 3 秒、关闭超时改成 100 毫秒,可验证强制超时分支。