endless

package module
v0.0.0-...-d773d36 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2016 License: MIT Imports: 14 Imported by: 0

README

endless

Zero downtime restarts for golang HTTP and HTTPS servers. (for golang 1.3+)

GoDoc

Inspiration & Credits

Well... it's what you want right - no need to hook in and out on a loadbalancer or something - just compile, SIGHUP, start new one, finish old requests etc.

There is https://github.com/rcrowley/goagain and i looked at https://fitstar.github.io/falcore/hot_restart.html which looked easier to do, but still some assembly required. I wanted something that's ideally as simple as

err := endless.ListenAndServe("localhost:4242", mux)

I found the excellent post Graceful Restart in Golang by Grisha Trubetskoy and took his code as a start. So a lot of credit to Grisha!

Features

  • Drop-in replacement for http.ListenAndServe and http.ListenAndServeTLS
  • Signal hooks to execute your own code before or after the listened to signals (SIGHUP, SIGUSR1, SIGUSR2, SIGINT, SIGTERM, SIGTSTP)
  • You can start multiple servers from one binary and endless will take care of the different sockets/ports assignments when restarting

Default Timeouts & MaxHeaderBytes

There are three variables exported by the package that control the values set for DefaultReadTimeOut, DefaultWriteTimeOut, and MaxHeaderBytes on the inner http.Server:

DefaultReadTimeOut    time.Duration
DefaultWriteTimeOut   time.Duration
DefaultMaxHeaderBytes int

The endless default behaviour is to use the same defaults defined in net/http.

These have impact on endless by potentially not letting the parent process die until all connections are handled/finished.

Termination

To deal with hanging requests on the parent after restarting endless will terminate the parent 60 seconds after receiving the shutdown signal from the forked child process. When terminated still running requests get terminated. This behaviour can be controlled by another exported variable:

DefaultTerminateTimeout time.Duration

The default is 60 seconds. When set to -1 Terminate() is not invoked automatically. You can then terminate the parent manually by sending SIGUSR2. This will only terminate the parent if it is already in shutdown mode. So unless the process had received a SIGTERM, SIGSTOP, or SIGINT (manually or by forking) before SIGUSR2 will be ignored.

If you had hanging requests and the server got terminate you will see a log message like this:

2015/04/04 13:04:10 Terminating parent

Examples & Documentation

import "github.com/fvbock/endless"

and then replacing http.ListenAndServe with endless.ListenAndServe or http.ListenAndServeTLS with endless.ListenAndServeTLS

err := endless.ListenAndServe("localhost:4242", handler)

After starting your server you can make some changes, build, and send SIGHUP to the running process and it will finish handling any outstanding requests and serve all new incoming ones with the new binary.

More examples are in here

There is also GoDoc Documentation

Signals

The endless server will listen for the following signals: syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGINT, syscall.SIGTERM, and syscall.SIGTSTP:

SIGHUP will trigger a fork/restart

syscall.SIGINT and syscall.SIGTERM will trigger a shutdown of the server (it will finish running requests)

SIGUSR2 will trigger Terminate

SIGUSR1 and SIGTSTP are listened for but do not trigger anything in the endless server itself. (probably useless - might get rid of those two)

You can hook your own functions to be called pre or post signal handling - eg. pre fork or pre shutdown. More about that in the hook example.

Limitation: No changing of ports

Currently you cannot restart a server on a different port than the previous version was running on.

PID file

If you want to save actual pid file, you can change the BeforeBegin hook like this:

server := endless.NewServer("localhost:4242", handler)
server.BeforeBegin = func(add string) {
	log.Printf("Actual pid is %d", syscall.Getpid())
	// save it somehow
}
err := server.ListenAndServe()

TODOs

  • tests
  • documentation
  • less ugly wrapping of the tls.listener

Documentation

Overview

Package endless provides a drop in replacement for the `net/http` standard library functions `ListenAndServe` and `ListenAndServeTLS` to achieve zero downtime restarts and code upgrades.

The code is based on the excellent post [Graceful Restart in Golang](http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/) by [Grisha Trubetskoy](https://github.com/grisha). I took his code as a start. So a lot of credit to Grisha!

Index

Constants

This section is empty.

Variables

View Source
var (
	// DefaultReadTimeout is the default value assigned to ReadTimeout of Servers.
	DefaultReadTimeout time.Duration

	// DefaultWriteTimeout is the default value assigned to WriteTimeout of Servers.
	DefaultWriteTimeout time.Duration

	// DefaultMaxHeaderBytes is the default value assigned to MaxHeaderBytes of Servers.
	DefaultMaxHeaderBytes int

	// DefaultTerminateTimeout is the default value assigned to TerminateTimeout of Servers.
	DefaultTerminateTimeout = 60 * time.Second
)

Default values used on server creation.

View Source
var ErrNoHandler = errors.New("no handler")

ErrNoHandler is the error returned if no Handler was configured on the Server.

View Source
var ErrRestartInProgress = errors.New("already forked")

ErrRestartInProgress is the error returned if Restart is called while one is already progress.

Functions

func ListenAndServe

func ListenAndServe(addr string, handler http.Handler) error

ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle requests on incoming connections. Handler is typically nil, in which case the DefaultServeMux is used.

func ListenAndServeTLS

func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error

ListenAndServeTLS acts identically to ListenAndServe, except that it expects HTTPS connections. Additionally, files containing a certificate and matching private key for the server must be provided. If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate followed by the CA's certificate.

func Restart

func Restart() (*os.Process, error)

Restart calls Restart on the default manager.

func SetHandler

func SetHandler(h Handler)

SetHandler calls SetHandler on the default manager.

func Shutdown

func Shutdown() error

Shutdown calls Shutdown on the default manager.

func Terminate

func Terminate(d time.Duration) error

Terminate calls Terminate on the default manager.

Types

type ErrUnsupportedListener

type ErrUnsupportedListener struct {
	net.Listener
}

ErrUnsupportedListener is the error returned when the Listener doesn't support a File method.

func (*ErrUnsupportedListener) Error

func (e *ErrUnsupportedListener) Error() string

type Handler

type Handler interface {
	Handle(*Manager)
	Stop()
}

Handler is the interface that objects implement to perform operations on a endless Servers.

type InvalidStateError

type InvalidStateError struct {
	CurrentState   State
	RequestedState State
}

InvalidStateError is the error type returned if an invalid state transition is attempted.

func (*InvalidStateError) Error

func (err *InvalidStateError) Error() string

type Listener

type Listener struct {
	net.Listener
	// contains filtered or unexported fields
}

Listener represents an endless listener.

func NewListener

func NewListener(l net.Listener, srv *Server) *Listener

NewListener creates a new Listener.

func (*Listener) Accept

func (el *Listener) Accept() (net.Conn, error)

Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.

func (*Listener) File

func (el *Listener) File() (*os.File, error)

File returns an os.File duplicated from the listener.

func (*Listener) Terminate

func (el *Listener) Terminate()

Terminate closes all active connections accepted by the listener.

type Manager

type Manager struct {
	*log.Logger

	Debug bool
	// contains filtered or unexported fields
}

Manager is the responsible for managing Servers.

func (*Manager) Debugln

func (m *Manager) Debugln(v ...interface{})

Debugln calls Println on Logger with the current pid prepended if Debug is true

func (*Manager) Listen

func (m *Manager) Listen(s *Server) (net.Listener, error)

Listen returns a listener created from the socket it was passed when restarted or a new listener created from the details given.

func (*Manager) Register

func (m *Manager) Register(srv *Server)

Register adds a server to the managers registered servers.

func (*Manager) Restart

func (m *Manager) Restart() (*os.Process, error)

Restart restarts the process with the new binary.

func (*Manager) SetHandler

func (m *Manager) SetHandler(h Handler)

SetHandler sets the active handler on the Manager. If the manager already had an active handler then its Stop method is called.

func (*Manager) Shutdown

func (m *Manager) Shutdown() error

Shutdown calls shutdown on all registered servers.

func (*Manager) Terminate

func (m *Manager) Terminate(d time.Duration) error

Terminate calls terminate on all registered servers.

func (*Manager) Unregister

func (m *Manager) Unregister(srv *Server)

Unregister removes a server to the managers registered servers.

type Server

type Server struct {
	http.Server

	BeforeBegin func(add string)

	TerminateTimeout time.Duration
	Done             chan struct{}
	Debug            bool
	Net              string
	// contains filtered or unexported fields
}

Server represents a endless server.

func NewServer

func NewServer(net, addr string, handler http.Handler) (srv *Server)

NewServer returns an initialized Server Object. Calling Serve on it will actually "start" the server.

func (*Server) AddressKey

func (srv *Server) AddressKey() string

AddressKey returns the unique address key for the server.

func (*Server) Debugln

func (srv *Server) Debugln(v ...interface{})

Debugln calls Println with the current pid prepended if Debug is true

func (*Server) GetState

func (srv *Server) GetState() State

GetState returns the current state of the server.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe() error

ListenAndServe listens on the TCP network address srv.Addr and then calls Serve to handle requests on incoming connections. If srv.Addr is blank, ":http" is used.

func (*Server) ListenAndServeTLS

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error)

ListenAndServeTLS listens on the TCP network address srv.Addr and then calls Serve to handle requests on incoming TLS connections.

Filenames containing a certificate and matching private key for the server must be provided. If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate followed by the CA's certificate.

If srv.Addr is blank, ":https" is used.

func (*Server) Printf

func (srv *Server) Printf(format string, v ...interface{})

Printf calls Printf on ErrorLog if not nil otherwise it calls log.Printf.

func (*Server) Println

func (srv *Server) Println(v ...interface{})

Println calls Println on ErrorLog if not nil otherwise it calls log.Println.

func (*Server) Serve

func (srv *Server) Serve() error

Serve accepts incoming HTTP connections on the Listener l, creating a new service goroutine for each. The service goroutines read requests and then call handler to reply to them. Handler is typically nil, in which case the DefaultServeMux is used.

In addition to the standard library Serve behaviour each connection is added to a sync.Waitgroup so that all outstanding connections can be served before shutting down the server.

func (*Server) Shutdown

func (srv *Server) Shutdown() error

Shutdown closes the Listener so that no new connections are accepted. it also starts a goroutine that will terminate (stop all running requests) the server after TerminateTimeout.

func (*Server) Terminate

func (srv *Server) Terminate(d time.Duration) error

Terminate forces the server to shutdown in a given timeout - whether it finished outstanding requests or not. if Read/WriteTimeout are not set or the max header size is very big a connection could hang...

srv.Serve() will not return until all connections are served. this will unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe(TLS) to return.

type SignalHandler

type SignalHandler struct {
	// contains filtered or unexported fields
}

SignalHandler listens for signals and takes action on the Manager.

By default: SIGHUP: calls Restart() SIGUSR2: calls Terminate(0), Shutdown() must have been called first. SIGINT & SIGTERM: calls Shutdown()

Pre and post signal handles can also be registered for custom actions.

func NewSignalHandler

func NewSignalHandler() *SignalHandler

NewSignalHandler create a new SignalHandler for the s

func (*SignalHandler) Handle

func (s *SignalHandler) Handle(m *Manager)

Handle listens for os.Signal's and calls any registered function hooks.

func (*SignalHandler) RegisterPostSignalHook

func (s *SignalHandler) RegisterPostSignalHook(sig os.Signal, f SignalHook)

RegisterPostSignalHook registers a function to be run after any built in signal action.

func (*SignalHandler) RegisterPreSignalHook

func (s *SignalHandler) RegisterPreSignalHook(sig os.Signal, f SignalHook)

RegisterPreSignalHook registers a function to be run before any built in signal action.

func (*SignalHandler) Stop

func (s *SignalHandler) Stop()

Stop stops the handler from taking any more action

type SignalHook

type SignalHook func(sig os.Signal) (cont bool)

SignalHook represents a signal processing hook. If false is returned no further processing of the signal is performed.

type State

type State uint8

State represents the current state of Server

const (
	// StateInit is the state of server when its created.
	StateInit State = iota

	// StateRunning is the state of server when its running.
	StateRunning

	// StateShuttingDown is the state of server when it is shutting down.
	StateShuttingDown

	// StateShutdown is the state of server when it has shutdown.
	StateShutdown

	// StateTerminated is the state of server if it was forcibly terminated.
	StateTerminated

	// StateFailed is the state of server if failed unexpectedly.
	StateFailed
)

func (State) String

func (s State) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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