2012-06-05 06:21:10 +08:00
|
|
|
// Package gracehttp provides easy to use graceful restart
|
|
|
|
// functionality for HTTP server.
|
|
|
|
package gracehttp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2013-08-21 02:29:40 +08:00
|
|
|
"crypto/tls"
|
2012-06-05 06:21:10 +08:00
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2013-08-21 02:29:40 +08:00
|
|
|
|
|
|
|
"github.com/daaku/go.grace"
|
2012-06-05 06:21:10 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2013-10-16 02:54:28 +08:00
|
|
|
verbose = flag.Bool("gracehttp.log", true, "Enable logging.")
|
|
|
|
errListenersCount = errors.New("unexpected listeners count")
|
|
|
|
errNoStartedListeners = errors.New("no started listeners")
|
2012-06-05 06:21:10 +08:00
|
|
|
)
|
|
|
|
|
2013-10-16 02:54:28 +08:00
|
|
|
// Defines an application containing various servers and associated
|
|
|
|
// configuration.
|
|
|
|
type App struct {
|
|
|
|
Servers []*http.Server
|
|
|
|
listeners []grace.Listener
|
|
|
|
errors chan error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inherit or create new listeners. Returns a bool indicating if we inherited
|
|
|
|
// listeners. This return value is useful in order to decide if we should
|
|
|
|
// instruct the parent process to terminate.
|
|
|
|
func (a *App) Listen() (bool, error) {
|
|
|
|
var err error
|
|
|
|
a.errors = make(chan error, len(a.Servers))
|
|
|
|
a.listeners, err = grace.Inherit()
|
|
|
|
if err == nil {
|
|
|
|
if len(a.Servers) != len(a.listeners) {
|
|
|
|
return true, errListenersCount
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
} else if err == grace.ErrNotInheriting {
|
|
|
|
if a.listeners, err = a.newListeners(); err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, fmt.Errorf("Failed graceful handoff: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates new listeners (as in not inheriting) for all the configured Servers.
|
|
|
|
func (a *App) newListeners() ([]grace.Listener, error) {
|
|
|
|
listeners := make([]grace.Listener, len(a.Servers))
|
|
|
|
for index, server := range a.Servers {
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", server.Addr)
|
2012-06-05 06:21:10 +08:00
|
|
|
if err != nil {
|
2013-10-16 02:54:28 +08:00
|
|
|
return nil, fmt.Errorf("net.ResolveTCPAddr %s: %s", server.Addr, err)
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
l, err := net.ListenTCP("tcp", addr)
|
|
|
|
if err != nil {
|
2013-10-16 02:54:28 +08:00
|
|
|
return nil, fmt.Errorf("net.ListenTCP %s: %s", server.Addr, err)
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
listeners[index] = grace.NewListener(l)
|
|
|
|
}
|
|
|
|
return listeners, nil
|
|
|
|
}
|
|
|
|
|
2013-10-16 02:54:28 +08:00
|
|
|
// Serve the configured servers, but do not block. You must call Wait at some
|
|
|
|
// point to ensure correctly waiting for graceful termination.
|
|
|
|
func (a *App) Serve() {
|
|
|
|
for i, l := range a.listeners {
|
2012-06-05 06:21:10 +08:00
|
|
|
go func(i int, l net.Listener) {
|
2013-10-16 02:54:28 +08:00
|
|
|
server := a.Servers[i]
|
|
|
|
|
|
|
|
// Wrap the listener for TLS support if necessary.
|
2013-08-21 02:29:40 +08:00
|
|
|
if server.TLSConfig != nil {
|
|
|
|
l = tls.NewListener(l, server.TLSConfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := server.Serve(l)
|
2012-06-05 06:21:10 +08:00
|
|
|
// The underlying Accept() will return grace.ErrAlreadyClosed
|
|
|
|
// when a signal to do the same is returned, which we are okay with.
|
|
|
|
if err != nil && err != grace.ErrAlreadyClosed {
|
2013-10-16 02:54:28 +08:00
|
|
|
a.errors <- fmt.Errorf("http.Serve: %s", err)
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
}(i, l)
|
|
|
|
}
|
2013-10-16 02:54:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the serving goroutines to finish.
|
|
|
|
func (a *App) Wait() error {
|
|
|
|
waiterr := make(chan error)
|
|
|
|
go func() { waiterr <- grace.Wait(a.listeners) }()
|
|
|
|
select {
|
|
|
|
case err := <-waiterr:
|
|
|
|
return err
|
|
|
|
case err := <-a.errors:
|
|
|
|
return err
|
|
|
|
}
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
|
2013-10-25 03:33:05 +08:00
|
|
|
// Serve will serve the given http.Servers and will monitor for signals
|
|
|
|
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
|
2013-05-10 00:28:09 +08:00
|
|
|
func Serve(servers ...*http.Server) error {
|
2013-10-16 02:54:28 +08:00
|
|
|
app := &App{Servers: servers}
|
|
|
|
inherited, err := app.Listen()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if *verbose {
|
|
|
|
if inherited {
|
2012-09-03 12:21:19 +08:00
|
|
|
ppid := os.Getppid()
|
|
|
|
if ppid == 1 {
|
2013-10-16 02:54:28 +08:00
|
|
|
log.Printf("Listening on init activated %s", pprintAddr(app.listeners))
|
2012-09-03 12:21:19 +08:00
|
|
|
} else {
|
2013-10-16 02:54:28 +08:00
|
|
|
const msg = "Graceful handoff of %s with new pid %d and old pid %d"
|
|
|
|
log.Printf(msg, pprintAddr(app.listeners), os.Getpid(), ppid)
|
2012-09-03 12:21:19 +08:00
|
|
|
}
|
2013-10-16 02:54:28 +08:00
|
|
|
} else {
|
|
|
|
const msg = "Serving %s with pid %d"
|
|
|
|
log.Printf(msg, pprintAddr(app.listeners), os.Getpid())
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 02:54:28 +08:00
|
|
|
|
|
|
|
app.Serve()
|
|
|
|
|
|
|
|
// Close the parent if we inherited and it wasn't init that started us.
|
|
|
|
if inherited && os.Getppid() != 1 {
|
|
|
|
if err := grace.CloseParent(); err != nil {
|
|
|
|
return fmt.Errorf("Failed to close parent: %s", err)
|
|
|
|
}
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
2013-10-16 02:54:28 +08:00
|
|
|
|
|
|
|
err = app.Wait()
|
|
|
|
|
2012-06-05 06:21:10 +08:00
|
|
|
if *verbose {
|
2012-09-03 12:21:19 +08:00
|
|
|
log.Printf("Exiting pid %d.", os.Getpid())
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
2013-10-16 02:54:28 +08:00
|
|
|
|
|
|
|
return err
|
2012-06-05 06:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Used for pretty printing addresses.
|
|
|
|
func pprintAddr(listeners []grace.Listener) []byte {
|
|
|
|
out := bytes.NewBuffer(nil)
|
|
|
|
for i, l := range listeners {
|
|
|
|
if i != 0 {
|
|
|
|
fmt.Fprint(out, ", ")
|
|
|
|
}
|
|
|
|
fmt.Fprint(out, l.Addr())
|
|
|
|
}
|
|
|
|
return out.Bytes()
|
|
|
|
}
|