In this blogpost I want to show you how you can make a http webserver in Go with gracefull shutdown. Using this approach you allow the server to clean up some resources before it actually shuts down. Think about finishing a database transaction or some other long operation. We will be using the things we learned in my blogpost on concurency. So expect to see channels and go routines as part of the solution.
When I create new http servers I usually start with an commandline flag to provide the port number to run my server. Especially when working on multiple microservices this comes in handy so you can run your webservers next to each other, to test the integration of those. Lets have a look at how we can provide a listen-address from the commandline when we start our server, including a sane default.
This will read a commandline option
-listen-addr and put the value in our variable
listenAddr. If no value is provided it will use
:5000 as default value. The text
server listen address will be used as description for the help text. So all commandline options you would like to have can be managed by using the flag package.
$ go build .
Now lets have a look at a basic setup of a webserver. In below example we create a new router which listens on
/ to respond with a http 200 status.
router := http.NewServeMux() // here you could also go with third party packages to create a router
In the last if statement we start our webserver and check for any errors. E.g. the given port could be in use and therefore not be able to start our webserver on the given port. If this happens it will log the error and stop the program. PLEASE NOTE: you have to add the
net/http package to your imports to make the code work at this stage.
When we now run our application you will see it will block at the line
server.ListenAndServe() until you kill the process.
So far so good and all working fine. However this would not gracefully shut down the server and any potential open connections to your webserver. Imagine someone is receiving a response from the server at the moment you kill the server, then also that response will immediately be killed. To allow the server to finish any open request we can build in some code to gracefully handle the work in progress with a max timeout. We will also change the server to not keep any connections that finish alive. To do so we will add some more code which runs on a separate go routine to intercept the signal to shutdown the application and do some gracefull handling of that.
First thing to do is add some channels which allow us to communicate between the 2 go routines. In case this is the first time you deal with routines and channels in Go you might want to checkout my blogpost on concurrency in Go first.
First we will define one channel to inform the main routine the gracefull shutdown finished. We will also add a channel which waits for any signals from the OS to shutdown our application.
In a separate Go routine we will wait for any interrupt signal (ctrl+c) to the
quit channel. First thing we will do, is to print a message on the console to inform the user the server is shutting down. Using a context we will then give the server 30 seconds to gracefully shutdown. With
server.SetKeepAlivesEnabled(false) we will inform the webserver to not keep any existing connections alive which basically gives us the gracefull shutdown behavior, instead of just slapping the door in front of a consumers face.
done := make(chan bool, 1)
Once shutdown is done we inform the main Go routine using the
done channel we finished the gracefull shutdown. This results in the program to continue execution to the last
logger.Println line to output the shutdown sequence fully completed and close the program.
Below you can see the full example of all the things we discussed in this blogpost combined in a fully working boilerplate.
As you can see I also did 2 small refactors where I moved the server creation and gracefull shutdown into their own methods. For the readers with an eagle eye you might also noticed I have made the channels in the function read and write only in the scope of the function which gives you a few more compile time advantages to prevent you from using the channels in a wrong manner. Last but not least you can also download the boilerplate here as a starting point for your own webserver.
Looking forward to your feedback. Please share this blog with your friends and colleagues on social media.