forked from goadesign/goa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgraceful.go
121 lines (107 loc) · 4.06 KB
/
graceful.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// +build !appengine
package goa
import (
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"gopkg.in/tylerb/graceful.v1"
)
// GracefulService is a goa application using a graceful shutdown server.
// When sending any of the signals listed in InterruptSignals to the process GracefulService:
//
// * disables keepalive connections.
//
// * closes the listening socket, allowing another process to listen on that port immediately.
//
// * calls Cancel, signaling all active handlers.
type GracefulService struct {
*Service
sync.Mutex
server *graceful.Server
// Interrupted is true if the application is in the process of shutting down.
Interrupted bool
// CancelOnShutdown tells whether existing requests should be canceled when shutdown is
// triggered (true) or whether to wait until the requests complete (false).
CancelOnShutdown bool
}
// InterruptSignals is the list of signals that initiate graceful shutdown.
// Note that only SIGINT is supported on Windows so this list should be
// overridden by the caller when running on that platform.
var InterruptSignals = []os.Signal{
os.Signal(syscall.SIGINT),
os.Signal(syscall.SIGTERM),
os.Signal(syscall.SIGQUIT),
}
// NewGraceful returns a goa application that uses a graceful shutdown server.
func NewGraceful(name string, cancelOnShutdown bool) *GracefulService {
service := New(name)
return &GracefulService{Service: service, CancelOnShutdown: cancelOnShutdown}
}
// ListenAndServe starts the HTTP server and sets up a listener on the given host/port.
func (serv *GracefulService) ListenAndServe(addr string) error {
serv.setup(addr)
Info(RootContext, "listen", KV{"address", addr})
if err := serv.server.ListenAndServe(); err != nil {
// there may be a final "accept" error after completion of graceful shutdown
// which can be safely ignored here.
if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
return err
}
}
return nil
}
// ListenAndServeTLS starts a HTTPS server and sets up a listener on the given host/port.
func (serv *GracefulService) ListenAndServeTLS(addr, certFile, keyFile string) error {
serv.setup(addr)
Info(RootContext, "listen ssl", KV{"address", addr})
return serv.server.ListenAndServeTLS(certFile, keyFile)
}
// Shutdown initiates graceful shutdown of the running server once. Returns true on
// initial shutdown and false if already shutting down.
func (serv *GracefulService) Shutdown() bool {
IncrCounter([]string{"goa", "graceful", "restart"}, 1.0)
serv.Lock()
defer serv.Unlock()
if serv.Interrupted {
return false
}
serv.Interrupted = true
serv.server.Stop(0)
if serv.CancelOnShutdown {
CancelAll()
}
return true
}
// setup initializes the interrupt handler and the underlying graceful server.
func (serv *GracefulService) setup(addr string) {
// we will trap interrupts here instead of allowing the graceful package to do
// it for us. the graceful package has the odd behavior of stopping the
// interrupt handler after first interrupt. this leads to the dreaded double-
// tap because the lack of any viable custom handler means that golang's
// default handler will kill the process on a second interrupt.
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, InterruptSignals...)
// Start interrupt handler goroutine
go func() {
for signal := range interruptChannel {
if serv.Shutdown() {
Info(RootContext, "Received signal. Initiating graceful shutdown...", KV{"signal", signal})
} else {
Info(RootContext, "Received signal. Already gracefully shutting down.", KV{"signal", signal})
}
}
}()
// note the use of zero timeout (i.e. no forced shutdown timeout) so requests
// can run as long as they want. there is usually a hard limit to when the
// response must come back (e.g. the nginx timeout) before being abandoned so
// the handler should implement some kind of internal timeout (e.g. the go
// context deadline) instead of relying on a shutdown timeout.
serv.server = &graceful.Server{
Timeout: 0,
Server: &http.Server{Addr: addr, Handler: serv.Mux},
NoSignalHandling: true,
}
}