Last active
May 7, 2023 04:56
-
-
Save katallaxie/2c5624c70cda289aa9f7bed7de126729 to your computer and use it in GitHub Desktop.
Server in Go with multiple listeners and signals.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package server | |
import ( | |
"context" | |
"os" | |
"os/signal" | |
"syscall" | |
"time" | |
"golang.org/x/sync/errgroup" | |
) | |
// Server is the interface to the server | |
type Server interface { | |
// Run is running a new routine | |
Listen(listener Listener) | |
// Waits for the server to fail, | |
// or gracefully shutdown | |
Wait() error | |
} | |
// Opt ... | |
type Opt func(*Opts) | |
// Opts ... | |
type Opts struct { | |
// ReloadSignal | |
ReloadSignal syscall.Signal | |
// TermSignal | |
TermSignal syscall.Signal | |
// KillSignal | |
KillSignal syscall.Signal | |
} | |
type listeners map[Listener]bool | |
// server holds the instance info of the server | |
type server struct { | |
errGroup *errgroup.Group | |
errCtx context.Context | |
cancel context.CancelFunc | |
listeners map[Listener]bool | |
sys chan os.Signal | |
opts *Opts | |
} | |
// NewServer .... | |
func NewServer(ctx context.Context, opts ...Opt) Server { | |
options := &Opts{} | |
s := new(server) | |
s.opts = options | |
ctx, cancel := context.WithCancel(ctx) | |
s.cancel = cancel | |
s.errGroup, s.errCtx = errgroup.WithContext(ctx) | |
s.listeners = make(listeners) | |
configure(s, opts...) | |
configureSignals(s) | |
return s | |
} | |
// Listen ... | |
func (s *server) Listen(listener Listener) { | |
if _, found := s.listeners[listener]; found { | |
return | |
} | |
s.listeners[listener] = true | |
g := s.errGroup | |
g.Go(listener.Start()) | |
} | |
// Wait ... | |
func (s *server) Wait() error { | |
// create ticker for interrupt signals | |
ticker := time.NewTicker(1 * time.Second) | |
ctx := s.errCtx | |
for { | |
select { | |
case <-ticker.C: | |
case <-s.sys: | |
s.cancel() | |
case <-ctx.Done(): | |
for listener := range s.listeners { | |
listener.Stop() | |
} | |
if err := ctx.Err(); err != nil { | |
return err | |
} | |
return nil | |
} | |
} | |
} | |
// Listener is the interface to a listener, | |
// so starting and shutdown of a listener, | |
// or any routine. | |
type Listener interface { | |
Start() func() error | |
Stop() error | |
} | |
func configureSignals(s *server) { | |
s.sys = make(chan os.Signal, 1) | |
signal.Notify(s.sys, s.opts.ReloadSignal, s.opts.KillSignal, s.opts.TermSignal) | |
} | |
func configure(s *server, opts ...Opt) error { | |
for _, o := range opts { | |
o(s.opts) | |
} | |
if s.opts.Env == "" { | |
s.opts.Env = env.Env | |
} | |
s.opts.TermSignal = syscall.SIGTERM | |
s.opts.ReloadSignal = syscall.SIGHUP | |
s.opts.KillSignal = syscall.SIGINT | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment