How to safely shutdown a server in Go

Written by arriqaaq | Published 2018/06/21
Tech Story Tags: golang | go | servers | devops | https

TLDRvia the TL;DR App

Image source: https://arstechnica.com/gaming/2014/02/the-day-the-mario-kart-died-nintendos-killswitch-and-the-future-of-online-consoles/

The net/http is one of the most important packages in Go. Almost every microservice written uses this default http package to run their servers. It takes a few lines of code to start a simple http server.

The package is more complex than mentioned above, but it provides the necessary parameters to tweak to run a high performing server.

In a highly critical microservice, where each request is critical, rolling update (or a server restart) is an important task for a service deployed at large scale, across multiple clusters. While doing an update/restart, one must ensure the existing requests being served are not killed abruptly on process restart. There are multiple ways in which developers achieve this, here a few:

  • When doing an rolling update for a service running on various clusters/k8 pods, you take each server/pod off the load balancer so that no new requests are served, drain the existing requests or wait for them to complete, and then update or restart your service. Like in Haproxy, you can set the maintenance mode to DRAIN, which does exactly the same.
  • Let the webserver handle the connections to be drained, and not kill/stop the process if any active connections are present.

There is a common trend you would see in many examples on Go’s http server module, which state the following convention:

With this approach, one would ideally be killing all requests which are currently being processed, as the quit signal would exit the process. This is wrong, in a way, because the existing requests did not get a chance to be served.

Prior to Go 1.8, one would give a clean up timeout, for which the server would wait for, before exiting the process to handle the completion of the existing requests. Since Go 1.8, the net/http package has an inbuilt Shutdown method which handles the graceful shutdown of a server. To quote from the GoDoc,

Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context’s error, otherwise it returns any error returned from closing the Server’s underlying Listener(s).

This inbuilt method helps in handling the graceful shutdown of the http server. Since I had been using the clean time out variable, which is nothing but a graceful shutdown time to wait for before killing the process, and everyone across the company would have a different implementation of the same, I wrote down this small wrapper on the net/http library which handles serving requests and shut down of a server on the required SYSCALL without having to explicitly write code to handle interrupts to shutdown a server. Also implements the basic SO_REUSEPORT functionality to fork multiple child processes to listen to the same port, and act as multiple workers behind the same port. Now the code becomes as simple as this:

arriqaaq/server_Simple wrapper around Golang net/http to start/shut a server_github.com

Let me know your thoughts on the handling the same issue.

P.S Open to suggestions/corrections!


Published by HackerNoon on 2018/06/21