当 SIGTERM 触发时停止 main.go

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

switch 语句中的 SIGTERM 情况不会被调用,并且“触发”不会被打印出来。

这是我的代码

func main() {
    port := os.Getenv("PORT")
    fmt.Printf("Startedn")

    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)
}

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

stack overflow Stop main.go when SIGTERM triggered
原文答案
author avatar

接受的答案

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


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

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


随着当前的临近,尝试这种方式:

func main() {
    port := os.Getenv("PORT")
    fmt.Printf("Startedn")

    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
        }
    }
}

更新:

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) go func() { for sig := range signalChanne { switch sig { case os.Interrupt: //还没有案例 syscall.SIGTERM: fmt.Printf("triggeredn") os.Exit(1) } } }()

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