Golang net http Server如何优雅关闭_HTTP服务关闭流程

必须用 Shutdown() 而不是 Close(),因为 Close() 会强制断开已接受但未响应的请求,而 Shutdown() 先拒新连、再等旧连完成或超时、最后强制关闭,配合 context.WithTimeout() 避免永久阻塞。

golang net http server如何优雅关闭_http服务关闭流程

Go 的 http.Server 优雅关闭,核心就是用 Shutdown() 配合 context 超时,而不是 Close()os.Exit() —— 后两者会直接中断正在处理的请求,造成客户端收到 connection reset 或超时失败。

为什么必须用 Shutdown() 而不是 Close()

Close() 立即关闭 listener,所有新连接被拒,但**已接受但尚未响应的请求会被强制断开**;而 Shutdown() 会:

  • 立刻拒绝新连接(关闭 listener)
  • 允许已建立的连接继续处理,直到完成或超时
  • 主动调用每个活跃连接的 conn.Close()(如果未完成)
  • 等待所有连接进入 idle 状态,或 context 被 cancel

不加 context 超时的 Shutdown(context.Background()) 可能永久阻塞(比如 handler 里有 select{} 死循环),所以必须配 context.WithTimeout()

信号监听与 goroutine 启动顺序不能反

常见错误是把 srv.ListenAndServe() 放在 main 协程里——它会阻塞,导致后续 signal.NotifyShutdown() 根本没机会执行。正确做法是:

立即学习go语言免费学习笔记(深入)”;

Viggle AI

Viggle AI

Viggle AI是一个AI驱动的3D动画生成平台,可以帮助用户创建可控角色的3D动画视频。

下载

  • go func() { srv.ListenAndServe() }() 异步启动服务
  • 主 goroutine 立即注册信号:signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  • 阻塞等待信号:,再触发 Shutdown()

注意:ListenAndServe() 返回 http.ErrServerClosed 是正常退出,不是错误,要显式忽略。

完整可运行示例(含耗时 handler 模拟 + 超时兜底)

package main

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

func main() { http.HandleFunc("/", func(w http.ResponseWriter, r http.Request) { log.Println("→ 开始处理请求(模拟 3s 业务)") time.Sleep(3 time.Second) fmt.Fprintln(w, "OK") log.Println("← 请求处理完成") })

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

// 启动服务(非阻塞)
go func() {
    log.Println("Server starting on :8080")
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatalf("server exited unexpectedly: %v", err)
    }
}()

// 监听系统信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Received shutdown signal")

// 执行优雅关闭(5s 超时)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
    log.Printf("Server forced to shutdown: %v", err)
} else {
    log.Println("Server gracefully stopped")
}

}

运行后按 Ctrl+C,你会看到:正在处理的请求走完才关,且不会超过 5 秒;如果 handler 卡死(如写死循环),也会在 5 秒后强制退出。真正的优雅,不是“等它自己停”,而是“给它机会停,但不无限等”。

https://www.php.cn/faq/1973159.html

发表回复

Your email address will not be published. Required fields are marked *