Stop main.go when SIGTERM triggered

2.9k Views Asked by At

I'm having trouble quitting my main.go app when the SIGTERM event is triggered.

The SIGTERM case in the switch statement doesn't get called and the "triggered" is not printed out.

Here is my code

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

I've tried the following solution but can't get it to work.

2

There are 2 best solutions below

0
On BEST ANSWER

Because the http.ListenAndServe() is running on the main goroutine. It will lock the process to serve the http server.


So no matter what you do inside another goroutine, it doesn't take effect, except you can try to kill that server. To do that, you must get the reference to the server to call server.Shutdown().

As pointing out this statement, I believe you're trying to shutdown gracefully by catching system event. Although things like os.Exit() or panic can do the job but it's a bad practice when http.Server is running. It'd be better to keep a reference to the running server to call server.Shutdown() with context.


With current approaching, try this way:

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

Updated:

A common reason that fmt.Println("sigterm") doesn't show up is that executing by go run, because the process is running in a subprocess. Kill go run will just send the signal to it and the subprocess will be terminated. Let's try with go build to see what happen exactly.

1
On

Your code is close to achieving the goal stated in the question. Loop receiving signals to handle the case where SIGINT is received before SIGTERM. If you want to exit the process on SIGTERM, then do so:

signalChannel := make(chan os.Signal, 2) signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) go func() { for sig := range signalChanne { switch sig { case os.Interrupt: //nothing yet case syscall.SIGTERM: fmt.Printf("triggered\n") os.Exit(1) } } }()

This program will print "triggered" if a SIGTERM is received.