欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

技术学院

Golang网络服务如何优雅关闭

作者:P粉6029986702026-01-05 00:00:00
必须用 http.Server.Shutdown 配超时 context 才是优雅关闭;Close() 立即关闭 listener,中断所有连接,不等待请求完成。

必须用 http.Server.Shutdown,且必须配超时 context —— 否则服务可能永远关不掉。

为什么 server.Close() 不是优雅关闭?

它会立刻关闭 listener,强制中断所有未完成的连接,正在处理的请求直接丢弃,客户端收到 connection reset 或空响应。这不是“优雅”,是“粗暴终止”。Shutdown() 才是 Go 1.8+ 官方定义的优雅路径:拒绝新连接、等待存量请求自然结束、再释放资源。

  • server.Close() → 立即返回,不等请求
  • server.Shutdown(ctx) → 阻塞直到 ctx 超时或所有连接干净退出
  • 若 handler 里有死循环(如 select{})、没设超时的 I/O、或阻塞 channel 操作,Shutdown() 会卡住

信号监听必须同时捕获 SIGINTSIGTERM

本地开发按 Ctrl+C 发的是 SIGINT;Kubernetes、systemd、Docker 等生产环境发的是 SIGTERM。只监听一个,会导致一种场景下无法触发关闭逻辑。

  • 别漏掉 syscall.SIGTERM,尤其上线后常被忽略
  • 不用监听 SIGKILL(即 kill -9)—— 它无法被捕获,也不该被处理
  • signal.Notify(quit, os.Interrupt, syscall.SIGTERM) 是最小可靠集合

Shutdown() 的 context 超时时间不是越长越好

设 5 秒还是 30 秒,取决于你最长请求的合理耗时。太短 → 强制中断未完成请求;太长 → 运维等待焦虑,CI/CD 流水线卡住,甚至触发平台级强杀。

  • 推荐从 10 * time.Second 起步,上线后根据监控(如 P99 请求时长)调整
  • 务必 defer cancel(),避免 context 泄漏
  • 错误示例:srv.Shutdown(context.Background()) —— 没超时兜底,一旦有异常请求就永久 hang 住
package main

import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" )

func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r http.Request) { time.Sleep(2 time.Second) // 模拟业务处理 w.Write([]byte("OK")) })

srv := &http.Server{
    Addr:    ":8080",
    Handler: mux,
}

go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatal(err)
    }
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)

zuojiankuohaophpcn-quit
log.Println("Received shutdown signal")

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := srv.Shutdown(ctx); err != nil {
    log.Printf("Server shutdown error: %v", err)
}
log.Println("Server exited gracefully")

}

真正难的不是写这几行代码,而是确保每个 handler 都尊重 context(比如用 r.Context() 做数据库查询超时)、没有无界 goroutine、第三方库调用也支持取消 —— 这些地方一漏,Shutdown() 就形同虚设。