Shutting down the application gracefully
Servers and daemons are the programs that run for a long time (typically days or even weeks). These long-running programs usually allocate resources (database connections, network sock) at the start and keep these resources as long as they exist. If such a process is killed and the shutdown is not handled properly, a resource leak could happen. To avoid that behavior, the so-called graceful shutdown should be implemented.
Graceful, in this case, means that the application catches the termination signal, if possible, and tries to clean up and release the allocated resources before it terminates. This recipe will show you how to implement the graceful shutdown.
The recipe, Handling operating system signals describes the catching of OS signals. The same approach will be used for implementing the graceful shutdown. Before the program terminates, it will clean up and carry out some other activities.
How to do it…
- Open the console and create the folder
chapter01/recipe11
. - Navigate to the directory.
- Create the
main.go
file with the following content:
package main import ( "fmt" "io" "log" "os" "os/signal" "syscall" "time" ) var writer *os.File func main() { // The file is opened as // a log file to write into. // This way we represent the resources // allocation. var err error writer, err = os.OpenFile(fmt.Sprintf("test_%d.log", time.Now().Unix()), os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { panic(err) } // The code is running in a goroutine // independently. So in case the program is // terminated from outside, we need to // let the goroutine know via the closeChan closeChan := make(chan bool) go func() { for { time.Sleep(time.Second) select { case <-closeChan: log.Println("Goroutine closing") return default: log.Println("Writing to log") io.WriteString(writer, fmt.Sprintf("Logging access %s\n", time.Now().String())) } } }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT) // This is blocking read from // sigChan where the Notify function sends // the signal. <-sigChan // After the signal is received // all the code behind the read from channel could be // considered as a cleanup. // CLEANUP SECTION close(closeChan) releaseAllResources() fmt.Println("The application shut down gracefully") } func releaseAllResources() { io.WriteString(writer, "Application releasing all resources\n") writer.Close() }
- Run the code by executing
go run main.go
. - Press CTRL + C to send a
SIGINT
signal. - Wait until the Terminal output looks like this:

- The
recipe11
folder should also contain a file calledtest_XXXX.log
, which contains lines like this:

How it works…
The reading from a sigChan
is blocking so the program keeps running until the Signal is sent through the channel. The sigChan
is the channel where the Notify
function sends the signals.
The main code of the program runs in a new goroutine
. This way, the work continues while the main function is blocked on the sigChan
. Once the signal from operation system is sent to process, the sigChan
receives the signal and the code below the line where the reading from the sigChan
channel is executed. This code section could be considered as the cleanup section.
Note that the step 7 terminal output contains the final log, Application releasing all resources
, which is part of the cleanup section.
See also
A detailed description of how the signal catching works is in the recipe Handling operating system signals.