触发 SIGTERM 时停止 main.go

Stop main.go when SIGTERM triggered

当 SIGTERM 事件被触发时,我无法退出我的 main.go 应用程序。

switch 语句中的 SIGTERM case 没有被调用并且“triggered”没有被打印出来。

这是我的代码

func main() {
    port := os.Getenv("PORT")
    fmt.Printf("Started\n")

    if port == "" {
        port = "8080"
    }

    signalChannel := make(chan os.Signal, 2)
    signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
    go func() {
        sig := <-signalChannel
        switch sig {
        case os.Interrupt:
            //nothing yet
        case syscall.SIGTERM:
            fmt.Printf("triggered") //never gets called
        }
    }()

    http.HandleFunc("/", HelloServer)
    http.ListenAndServe(":" + port, nil)
}

我尝试了以下解决方案,但无法正常工作。

因为 http.ListenAndServe() 在主 goroutine 上是 运行。它将锁定进程以服务于 http 服务器。


所以无论你在另一个 goroutine 中做什么,它都不会生效,除非你可以尝试杀死那个服务器。为此,您必须获得对服务器的引用才能调用 server.Shutdown().

正如您指出的那样,我相信您正试图通过捕获系统事件来正常关机。尽管 os.Exit()panic 之类的东西可以完成这项工作,但是当 http.Server 是 运行 时,这是一个不好的做法。最好保留对 运行 服务器的引用,以便使用上下文调用 server.Shutdown()


随着电流的临近,试试这个方法:

func main() {
    port := os.Getenv("PORT")
    fmt.Printf("Started\n")

    if port == "" {
        port = "8080"
    }

    go func() {
        http.HandleFunc("/", HelloServer)
        if err := http.ListenAndServe(":"+port, nil); err != nil {
            log.Fatal(err)
        }
    }()

    signalChannel := make(chan os.Signal, 2)
    signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
    for {
        sig := <-signalChannel
        switch sig {
        case os.Interrupt:
            fmt.Println("sigint")
        case syscall.SIGTERM:
            fmt.Println("sigterm")
            return
        }
    }
}

更新: 我不确定为什么反对票是因为这个问题想准确地抓住 SIGTERM,而不是通过 Ctrl+C (SIGINT)

fmt.Println("sigterm") 未显示的一个常见原因是 go run 执行,因为该进程在子进程中 运行。 Kill go run 只会向它发送信号,子进程将被终止。让我们尝试使用 go build 看看到底发生了什么。

您的代码已接近实现问题中所述的目标。 循环接收信号以处理在 SIGTERM 之前接收到 SIGINT 的情况。如果你想在 SIGTERM 上退出进程,那么这样做:

signalChannel := make(chan os.Signal, 2) signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) 去功能(){ 对于 sig := 范围 signalChanne { 开关信号{ 案例os.Interrupt: //还没有 案例syscall.SIGTERM: fmt.Printf("triggered\n") os.Exit(1) } } }()

如果收到 SIGTERM,此程序将打印“已触发”。