首页 > 解决方案 > 当 SIGTERM 触发时停止 main.go

问题描述

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

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

这是我的代码

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

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

标签: godredd

解决方案


因为在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") 未显示的一个常见原因是执行 by go run,因为该进程正在子进程中运行。Killgo run只会向它发送信号,并且子进程将被终止。让我们试着go build看看到底发生了什么。


推荐阅读