Skip to content

Instantly share code, notes, and snippets.

@knadh
Last active July 14, 2020 05:30
Show Gist options
  • Save knadh/798fe3bf116af43d6647c0524f6519c2 to your computer and use it in GitHub Desktop.
Save knadh/798fe3bf116af43d6647c0524f6519c2 to your computer and use it in GitHub Desktop.
Example of gracefully shutting down and re-loading / restarting a Go app on SIGHUP retaining the same PID
// program
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
// Compile and run this program and send SIGHUP. eg: pkill -9 go-graceful-reload
fmt.Printf("Welcome: %s: pid is %d\n", os.Args, os.Getpid())
// Everything happens here. HTTP servers and other blocking calls, should
// be invoked as goroutines.
// srv := http.ListenAndServe()
// This should be at the end of main().
cleanupWait := make(chan bool)
<-awaitReload(cleanupWait, func() {
// This callback can be used to gracefully close resources like
// HTTP servers, DB connections etc.
// srv.Close()
fmt.Println("reloading!")
// After closing everything, send the ready signal.
cleanupWait <- true
})
}
func awaitReload(closerWait chan bool, closer func()) chan bool {
// The blocking signal handler that main() waits on.
out := make(chan bool)
// Respawn a new process and exit the running one. syscall.Exec() retains
// the original PID.
respawn := func() {
if err := syscall.Exec(os.Args[0], os.Args, os.Environ()); err != nil {
log.Fatalf("error spawning process: %v", err)
}
os.Exit(0)
}
// Listen for reload signals.
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
go func() {
for range c {
log.Println("got reload signal. restarting ...")
go closer()
select {
case <-closerWait:
// Wait for the closer to finish.
respawn()
case <-time.After(time.Second * 3):
// Or timeout and force close.
respawn()
}
}
}()
return out
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment