Package to manage services start and graceful shutdown synchronize.
- Implementing
Service interface
type Service interface {
WithShutdownSignal(shutdown <-chan struct{}, done chan<- struct{})
Start() error
Name() string
}
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"github.com/dohernandez/goservicing"
)
// service implementing the interface goservicing.Service
type service struct {
// use mainly to log the start of the service and in case the failure shutdown.
name string
addr string
shutdownSignal <-chan struct{}
shutdownDone chan<- struct{}
srv *http.Server
}
func NewService() *service {
return &service{
name: "Service",
addr: "8080",
}
}
// WithShutdownSignal adds channels to wait for shutdown and to report shutdown finished.
func (s *service) WithShutdownSignal(shutdown <-chan struct{}, done chan<- struct{}) goservicing.Service {
s.shutdownSignal = shutdown
s.shutdownDone = done
return s
}
// Name Service name.
func (s *service) Name() string {
return s.name
}
// Addr service address.
func (s *service) Addr() string {
return s.addr
}
// Start begins listening and serving.
func (s *service) Start() error {
s.handleShutdown()
router := http.NewServeMux()
router.HandleFunc("/test", func(res http.ResponseWriter, req *http.Request) {
res.WriteHeader(200)
res.Write([]byte("Test is what we usually do"))
})
s.srv = &http.Server{
Handler: router,
Addr: fmt.Sprintf(":%s", s.addr),
}
return s.srv.ListenAndServe()
}
// handleShutdown will handle the shutdown signal that comes to the server
// and shutdown the server.
func (s *service) handleShutdown() {
if s.shutdownSignal == nil {
return
}
go func() {
<-s.shutdownSignal
if err := s.srv.Shutdown(context.Background()); err != nil {
_ = s.srv.Close() // nolint: errcheck
}
close(s.shutdownDone)
}()
}
func main() {
srv := NewService()
servicing := &goservicing.ServiceGroup{}
err := servicing.Start(
context.Background(),
time.Minute,
func(ctx context.Context, msg string) {
log.Println(msg)
},
srv,
)
log.Fatalln(err)
}
- Using
NewService
creates new instance of Service.
type Jop struct { ... }
// Run starts the job
func (j *Job) Run() error {...}
// Stop stops the job
func (j *Job) Stop() error {...}
j := &Job{}
sg := &ServiceGroup{}
err = servicing.Start(
context.Background(),
15*time.Second, // ttl is used during graceful shutdown.
func(ctx context.Context, msg string) {
log.Println(msg)
},
NewService(
"Job A",
func() error {
return j.Run()
},
func() error {
return j.Stop()
},
),
)
if err != nil {
panic(fmt.Errorf("failed to start servicing: %w", err))
}