Octopus

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2019 License: MIT Imports: 11 Imported by: 0

README

Octopus - Serve HTTP Server Gracefully

CircleCI

Octopus is a tool that help serve HTTP server gracefully. User can:

  • gracefully upgrade server (binary) without blocking incoming requests
  • gracefully shutdown server so that incoming requests can be handled or time-outed.

Credits

Octopus is greatly inspired by the article "Gracefully Restarting a Go Program Without Downtime" written by Russell Jones. A lot of credits should go to Russell.

Install

go get github.com/NBCFB/Octopus@lastest

If you use go 1.11+, you can just add import github.com/NBCFB/Octopus/1.0.1 and start using it. When you run go build or go test, the module will be downloaded and installed automatically.

You can check all the versions of Octopus using go list:

$ go list -m -versions github.com/NBCFB/Octopus

Example - HTTP Server

Create A Graceful HTTP Server

You simply call GracefulServe(...) by passing your http.Server. That's it. Here is an example.

package main

import (
	"flag"
	"fmt"
	"github.com/NBCFB/Octopus"
	"github.com/go-chi/chi"
	"net/http"
	"time"
)

func ping (w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("pong\n"))
}

func main() {
	var host string
	var port int

	// Handle command line flag
	flag.StringVar(&host, "h", "localhost", "specify host")
	flag.IntVar(&port, "p", 8080, "specify port")
	flag.Parse()

	// Make up addr with default settings
	addr := fmt.Sprintf("%s:%d", host, port)

	// Set up router
	r := chi.NewRouter()
	r.Get("/ping", ping)

	s := &http.Server{
		Addr: addr,
		WriteTimeout: time.Second * 15,
		ReadTimeout:  time.Second * 15,
		IdleTimeout:  time.Second * 60,
		Handler:      r,
	}

	// Start server
	Octopus.GracefulServe(s, false)
}

Now, we can start the server using go run from command line:

$ go run test_servers/simpleHTTPServer.go -h 172.18.1.239 -p 8080 &
[1] 52215
$ 2019/04/11 16:09:12 [INFO] Created a new listener on 172.18.1.239:8080.
2019/04/11 16:09:12 [INFO] The server has started (52233).

Now we can see that the server has started, listening on address 172.18.1.239:8080, where the pid is 52233. We can check if the server is running properly.

$ curl http://172.18.1.239:8080/ping
pong
Upgrade(Restart) The Server

Assume we have updated (using go build) the server binary. The change is shown as below:

func ping (w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("pong pong\n"))
}

We do not need perform a 'stop-and-start' process. We can simply send a SIGUSR1 or SIGUSR2 to gracefully restart the server without blocking incoming requests:

$ kill -SIGUSR1 52233
$ 2019/04/11 16:18:11 [INFO] Server (52233) received signal "user defined signal 1".
2019/04/11 16:18:11 [INFO] Forked child (55344).
2019/04/11 16:18:11 [INFO] Master (52233) is still alive.
2019/04/11 16:18:11 [INFO] Unable to import a listener from file: unable to find listener on localhost:8080. Trying to create a new one.
2019/04/11 16:18:11 [INFO] Created a new listener on localhost:8080.
2019/04/11 16:18:11 [INFO] The server has started (55344).

Now we can see that a child was forked whose pid is 55344. Since we have set killMaster(the second arg in Octopus.GracefulServe(...) to false, we keep the master alive. At this point, the incoming requests will be handled randomly by either the master or the child, as shown below:

$ curl http://172.18.1.239:8080/ping
pong pong
$ curl http://172.18.1.239:8080/ping
pong

You can set killMaster to true to kill the master after a child is successfully forked.

Now, we can kill the master so that all the incoming requests will be handled based on the updated server binary.

$ kill -SIGTERM 52233
$ 2019/04/11 16:24:54 [INFO] Server (52233) received signal "terminated".
2019/04/11 16:24:54 [INFO] Digesting requests will be timed out at 2019-04-11 16:24:59
2019/04/11 16:24:54 [INFO] The server has shut down.

The above output shows that the arrived requests will be still processed till 2019-04-11 16:24:59. Now, let's check if the server is updated.

$ curl http://172.18.1.239:8080/ping
pong pong

We can see that the server has been upgraded successfully without blocking any incoming request.

Signals

Octopus listens only preset hooked signals including syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGINT and syscall.SIGTERM. The corresponding behaviors are shown as below:

SIGNALS BEHAVIOR
kill -SIGNHUP Fork --> shut down.
kill -SIGUSR1 , kill -SIGUSR2 Fork
kill -SIGNINT , kill -SIGTERM Shut down

Example Server Codes

There are two example servers in /test_servers folder:

Documentation

Index

Constants

View Source
const (
	// DefaultEnvVar is the default environment variable
	DefaultEnvVar       = "OCTOPUS_LISTENER"
	DefaultNetwork      = "tcp"
	DefaultAwaitTimeout = 5 * time.Second
)

Variables

This section is empty.

Functions

func GracefulServeTLS

func GracefulServeTLS(server *http.Server, killMaster bool, certFile, keyFile string) (err error)

GracefulServeTLS starts a HTTPS server. It receives a http.Server server passed by user and an indicators killMaster. It first create a listener (either a new one or a imported one). Then it starts a goroutine for the server to start accepting connections. Any hooked signals will be handled in handleSignals(...). Certificate and key are compulsory for starting a HTTPS server.

func GracefulShutDown

func GracefulShutDown(server *http.Server) (err error)

GracefulShutDown shuts down a server with a timeout. In most case, you will not need use it.

Types

type GracefulServer

type GracefulServer struct {
	Addr     string
	PID      int
	Server   *http.Server
	Listener net.Listener
}

GracefulServer defines a HTTP server. The reason we do not make something like

type GracefulServer struct {
	http.Server
	...
}

is because we want to give user freedom to make their own and we can serve it as long as it is a http.Server.

func GracefulServe

func GracefulServe(server *http.Server, killMaster bool) (gs *GracefulServer, err error)

GracefulServe starts a HTTP server. It receives a http.Server server passed by user and an indicators killMaster. It first create a listener (either a new one or a imported one). Then it starts a goroutine for the server to start accepting connections. Any hooked signals will be handled in handleSignals(...).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL