I want to learn the proper way to gracefully shutdown a http.Server{}. Some answers I found would not handle ctrl-c signals. Others would handle it, but it would lock on the channel read if the server closed itself.
To handle the above situations, I came up with this template. I'm not very experienced when system signals, http.Server and concurrency are all mixed.
- Am I missing something?
- Could I remove the
sync.WaitGroup{},killandclosedchannels? And let the program exits and discard the goroutine?
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"sync"
)
func main() {
log.Print("main: start")
srv := http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello!!!")
}),
}
stop := make(chan os.Signal, 1)
kill := make(chan os.Signal, 1)
closed := make(chan struct{}, 1)
signal.Notify(stop, os.Interrupt)
signal.Notify(kill, os.Kill)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
log.Print("goroutine: start")
select {
case <-stop:
log.Print("goroutine: interrupt signal: server shutdown: start")
if err := srv.Shutdown(context.TODO()); err != nil {
log.Print("goroutine: interrupt signal: server shutdown: ", err)
}
log.Print("goroutine: interrupt signal: server shutdown: exit")
case <-kill:
log.Print("goroutine: kill signal")
// do nothing?
case <-closed:
log.Print("goroutine: server closed")
// do nothing
}
log.Print("goroutine: exit")
}()
log.Print("server: listen start")
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Print("server error: ", err)
}
log.Print("server: listen exit")
closed <- struct{}{}
wg.Wait()
log.Print("main: exit")
}
```