You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅关闭运行在Docker容器中的Go Web服务器?

Go Web服务在Docker中优雅关闭的实现验证

你的代码核心思路是可行的,但需要调整几个细节,才能完美适配Docker的容器停止流程,下面具体说明:


关键前提:Docker stop发送的信号

默认情况下,docker stop会先给容器内的主进程发送SIGTERM信号,等待10秒后如果进程未退出,会发送SIGKILL强制终止。所以你捕获SIGTERM是正确的,但建议同时监听SIGINT(比如容器内按Ctrl+C的场景),覆盖更多终止触发场景。


代码调整要点

  1. 信号监听补充
    修改信号捕获逻辑,同时监听SIGTERM和SIGINT:

    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
    
  2. ListenAndServe的错误处理
    原代码里的go srv.ListenAndServe()没有处理错误,除了http.ErrServerClosed(正常关闭的预期错误),其他错误需要捕获告警:

    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("服务启动失败: %v", err)
        }
    }()
    
  3. Shutdown的上下文优化
    调用srv.Shutdown时,不要用已经触发Done的原ctx,应该创建新的带超时的上下文(超时时间建议比Docker的10秒短,预留缓冲):

    shutdownCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
    defer cancel()
    err = srv.Shutdown(shutdownCtx)
    
  4. 清理逻辑的位置
    注释// do something的位置适合放置自定义清理操作,比如关闭数据库连接、停止后台异步任务等,要在Shutdown前完成必要的资源释放。


调整后的完整代码示例

package main

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

func main() {
    port := ":8080"
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })

    // 监听Docker stop的SIGTERM和手动中断的SIGINT
    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
    defer stop()

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

    // 启动服务并处理启动错误
    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("服务启动失败: %v", err)
        }
    }()

    // 等待终止信号
    <-ctx.Done()
    log.Println("收到停止信号,开始优雅关闭...")

    // 执行自定义清理操作(示例:关闭数据库连接)
    // db.Close()

    // 创建带超时的上下文,确保Shutdown有足够时间处理请求
    shutdownCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
    defer cancel()

    // 执行优雅关闭
    if err := srv.Shutdown(shutdownCtx); err != nil {
        log.Fatalf("服务关闭失败: %v", err)
    }

    log.Println("服务已优雅关闭")
}

总结

你的核心实现思路是正确的:通过捕获信号触发Server.Shutdown()来完成优雅关闭,只要调整上述细节,就能完美适配Docker容器的停止流程。

内容的提问来源于stack exchange,提问作者kkkkgyg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:02:24