Last active
July 14, 2020 05:30
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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