feat(cmd/transpilerd): update transpilerd to use http server package
The http package now contains a server that handles signals and proper shutdown procedure. It has now been updated to use it. The http package has also added a `ListenAndServe` convenience function that is similar to the `net/http` one, but also takes in a logger and will automatically use the most common signals when running an http server.pull/10616/head
parent
0c422a863f
commit
d8e4f4f2e0
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
nethttp "net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -61,7 +60,9 @@ func transpileF(cmd *cobra.Command, args []string) {
|
||||||
handler.Handler = transpileHandler
|
handler.Handler = transpileHandler
|
||||||
|
|
||||||
log.Printf("Starting transpilerd on %s\n", flags.bindAddr)
|
log.Printf("Starting transpilerd on %s\n", flags.bindAddr)
|
||||||
log.Fatal(nethttp.ListenAndServe(flags.bindAddr, handler))
|
if err := http.ListenAndServe(flags.bindAddr, handler, nil); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -24,7 +25,7 @@ type Server struct {
|
||||||
ShutdownTimeout time.Duration
|
ShutdownTimeout time.Duration
|
||||||
|
|
||||||
srv *http.Server
|
srv *http.Server
|
||||||
signalCh chan os.Signal
|
signals map[os.Signal]struct{}
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
@ -50,14 +51,17 @@ func (s *Server) Serve(listener net.Listener) error {
|
||||||
// When we return, wait for all pending goroutines to finish.
|
// When we return, wait for all pending goroutines to finish.
|
||||||
defer s.wg.Wait()
|
defer s.wg.Wait()
|
||||||
|
|
||||||
|
signalCh, cancel := s.notifyOnSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
errCh := s.serve(listener)
|
errCh := s.serve(listener)
|
||||||
select {
|
select {
|
||||||
case err := <-errCh:
|
case err := <-errCh:
|
||||||
// The server has failed and reported an error.
|
// The server has failed and reported an error.
|
||||||
return err
|
return err
|
||||||
case <-s.signalCh:
|
case <-signalCh:
|
||||||
// We have received an interrupt. Signal the shutdown process.
|
// We have received an interrupt. Signal the shutdown process.
|
||||||
return s.shutdown()
|
return s.shutdown(signalCh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +78,7 @@ func (s *Server) serve(listener net.Listener) <-chan error {
|
||||||
return errCh
|
return errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) shutdown() error {
|
func (s *Server) shutdown(signalCh <-chan os.Signal) error {
|
||||||
// The shutdown needs to succeed in 20 seconds or less.
|
// The shutdown needs to succeed in 20 seconds or less.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), s.ShutdownTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), s.ShutdownTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -87,7 +91,7 @@ func (s *Server) shutdown() error {
|
||||||
go func() {
|
go func() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
select {
|
select {
|
||||||
case <-s.signalCh:
|
case <-signalCh:
|
||||||
cancel()
|
cancel()
|
||||||
case <-done:
|
case <-done:
|
||||||
}
|
}
|
||||||
|
@ -96,10 +100,45 @@ func (s *Server) shutdown() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenForSignals registers the the server to listen for the given signals
|
// ListenForSignals registers the the server to listen for the given signals
|
||||||
// to shutdown the server.
|
// to shutdown the server. The signals are not captured until Serve is called.
|
||||||
func (s *Server) ListenForSignals(signals ...os.Signal) {
|
func (s *Server) ListenForSignals(signals ...os.Signal) {
|
||||||
if s.signalCh == nil {
|
if s.signals == nil {
|
||||||
s.signalCh = make(chan os.Signal, 4)
|
s.signals = make(map[os.Signal]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sig := range signals {
|
||||||
|
s.signals[sig] = struct{}{}
|
||||||
}
|
}
|
||||||
signal.Notify(s.signalCh, signals...)
|
}
|
||||||
|
|
||||||
|
func (s *Server) notifyOnSignals() (_ <-chan os.Signal, cancel func()) {
|
||||||
|
if len(s.signals) == 0 {
|
||||||
|
return nil, func() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve which signals we want to be notified on.
|
||||||
|
signals := make([]os.Signal, 0, len(s.signals))
|
||||||
|
for sig := range s.signals {
|
||||||
|
signals = append(signals, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the signal channel and mark ourselves to be notified
|
||||||
|
// of signals. Allow up to two signals for each signal type we catch.
|
||||||
|
signalCh := make(chan os.Signal, len(signals)*2)
|
||||||
|
signal.Notify(signalCh, signals...)
|
||||||
|
return signalCh, func() { signal.Stop(signalCh) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe is a convenience method for opening a listener using the address
|
||||||
|
// and then serving the handler on that address. This method sets up the typical
|
||||||
|
// signal handlers.
|
||||||
|
func ListenAndServe(addr string, handler http.Handler, logger *zap.Logger) error {
|
||||||
|
l, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
server := NewServer(handler, logger)
|
||||||
|
server.ListenForSignals(os.Interrupt, syscall.SIGTERM)
|
||||||
|
return server.Serve(l)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue