2020-01-22 14:02:06 +01:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2020-03-03 15:12:33 +01:00
|
|
|
"context"
|
2020-01-22 14:02:06 +01:00
|
|
|
"net"
|
|
|
|
"os"
|
2020-11-08 19:28:33 +01:00
|
|
|
"strings"
|
2020-01-22 14:02:06 +01:00
|
|
|
"sync"
|
2020-11-08 19:28:33 +01:00
|
|
|
"time"
|
2020-01-22 14:02:06 +01:00
|
|
|
|
2020-11-08 19:28:33 +01:00
|
|
|
"github.com/google/uuid"
|
2020-01-22 14:02:06 +01:00
|
|
|
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
|
|
|
"github.com/jinzhu/gorm"
|
2020-11-08 19:28:33 +01:00
|
|
|
"github.com/sirupsen/logrus"
|
2020-01-22 14:02:06 +01:00
|
|
|
"github.com/spf13/cobra"
|
2020-03-03 14:54:53 +01:00
|
|
|
"go.uber.org/multierr"
|
2020-01-22 14:02:06 +01:00
|
|
|
"google.golang.org/grpc"
|
2020-08-07 10:59:27 +02:00
|
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
"google.golang.org/grpc/reflection"
|
2020-11-08 19:28:33 +01:00
|
|
|
|
|
|
|
"gitlab.bertha.cloud/partitio/lab/grpc/registry"
|
|
|
|
"gitlab.bertha.cloud/partitio/lab/grpc/registry/noop"
|
|
|
|
"gitlab.bertha.cloud/partitio/lab/grpc/utils/addr"
|
|
|
|
"gitlab.bertha.cloud/partitio/lab/grpc/utils/backoff"
|
|
|
|
net2 "gitlab.bertha.cloud/partitio/lab/grpc/utils/net"
|
2020-01-22 14:02:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Service interface {
|
|
|
|
Options() Options
|
|
|
|
Server() *grpc.Server
|
|
|
|
DB() *gorm.DB
|
|
|
|
Start() error
|
|
|
|
Stop() error
|
|
|
|
Close() error
|
|
|
|
Cmd() *cobra.Command
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(opts ...Option) (Service, error) {
|
|
|
|
return newService(opts...)
|
|
|
|
}
|
|
|
|
|
|
|
|
type service struct {
|
2020-03-03 15:12:33 +01:00
|
|
|
cmd *cobra.Command
|
|
|
|
opts *options
|
|
|
|
cancel context.CancelFunc
|
|
|
|
server *grpc.Server
|
|
|
|
list net.Listener
|
|
|
|
mu sync.Mutex
|
2020-01-22 14:02:06 +01:00
|
|
|
running bool
|
2020-11-08 19:28:33 +01:00
|
|
|
|
|
|
|
id string
|
|
|
|
regSvc *registry.Service
|
|
|
|
closed chan struct{}
|
2020-01-22 14:02:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func newService(opts ...Option) (*service, error) {
|
|
|
|
cmd.ParseFlags(os.Args)
|
|
|
|
s := &service{
|
|
|
|
opts: parseFlags(NewOptions()),
|
|
|
|
cmd: cmd,
|
2020-11-08 19:28:33 +01:00
|
|
|
id: uuid.New().String(),
|
2020-01-22 14:02:06 +01:00
|
|
|
}
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
for _, f := range opts {
|
|
|
|
f(s.opts)
|
|
|
|
}
|
2020-03-03 14:54:53 +01:00
|
|
|
if s.opts.error != nil {
|
|
|
|
return nil, s.opts.error
|
|
|
|
}
|
2020-03-03 15:12:33 +01:00
|
|
|
s.opts.ctx, s.cancel = context.WithCancel(s.opts.ctx)
|
2020-01-22 14:02:06 +01:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-s.opts.ctx.Done():
|
|
|
|
s.Stop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2020-11-08 19:28:33 +01:00
|
|
|
if s.opts.registry == nil {
|
|
|
|
s.opts.registry = noop.New()
|
|
|
|
}
|
2020-08-07 10:59:27 +02:00
|
|
|
var err error
|
|
|
|
s.list, err = net.Listen("tcp", s.opts.address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-22 14:02:06 +01:00
|
|
|
if err := s.opts.parseTLSConfig(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cmd.Use = s.opts.name
|
|
|
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
|
|
|
if cmd.Use == "" {
|
|
|
|
cmd.Use = os.Args[0]
|
|
|
|
}
|
|
|
|
return s.run()
|
|
|
|
}
|
2020-08-07 10:59:27 +02:00
|
|
|
gopts := []grpc.ServerOption{
|
|
|
|
grpc.UnaryInterceptor(
|
|
|
|
grpcmiddleware.ChainUnaryServer(s.opts.serverInterceptors...),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
if s.opts.tlsConfig != nil {
|
2020-11-08 19:28:33 +01:00
|
|
|
gopts = append(gopts, grpc.Creds(credentials.NewTLS(s.opts.tlsConfig)))
|
2020-08-07 10:59:27 +02:00
|
|
|
}
|
2020-01-22 14:02:06 +01:00
|
|
|
s.server = grpc.NewServer(append(gopts, s.opts.serverOpts...)...)
|
2020-08-07 10:59:27 +02:00
|
|
|
if s.opts.reflection {
|
|
|
|
reflection.Register(s.server)
|
|
|
|
}
|
2020-01-22 14:02:06 +01:00
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Options() Options {
|
|
|
|
return s.opts
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) DB() *gorm.DB {
|
|
|
|
return s.opts.db
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Server() *grpc.Server {
|
|
|
|
return s.server
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Cmd() *cobra.Command {
|
|
|
|
return s.cmd
|
|
|
|
}
|
|
|
|
|
2020-11-08 19:28:33 +01:00
|
|
|
func (s *service) register() error {
|
|
|
|
const (
|
|
|
|
defaultRegisterInterval = time.Second * 30
|
|
|
|
defaultRegisterTTL = time.Second * 90
|
|
|
|
)
|
|
|
|
regFunc := func(service *registry.Service) error {
|
|
|
|
var regErr error
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
// set the ttl
|
|
|
|
rOpts := []registry.RegisterOption{registry.RegisterTTL(defaultRegisterTTL)}
|
|
|
|
// attempt to register
|
|
|
|
if err := s.opts.Registry().Register(service, rOpts...); err != nil {
|
|
|
|
// set the error
|
|
|
|
regErr = err
|
|
|
|
// backoff then retry
|
|
|
|
time.Sleep(backoff.Do(i + 1))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// success so nil error
|
|
|
|
regErr = nil
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
return regErr
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
var advt, host, port string
|
|
|
|
|
|
|
|
//// check the advertise address first
|
|
|
|
//// if it exists then use it, otherwise
|
|
|
|
//// use the address
|
|
|
|
//if len(config.Advertise) > 0 {
|
|
|
|
// advt = config.Advertise
|
|
|
|
//} else {
|
|
|
|
advt = s.opts.address
|
|
|
|
//}
|
|
|
|
|
|
|
|
if cnt := strings.Count(advt, ":"); cnt >= 1 {
|
|
|
|
// ipv6 address in format [host]:port or ipv4 host:port
|
|
|
|
host, port, err = net.SplitHostPort(advt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
host = s.opts.address
|
|
|
|
}
|
|
|
|
|
|
|
|
addr, err := addr.Extract(host)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// register service
|
|
|
|
node := ®istry.Node{
|
|
|
|
Id: s.opts.name + "-" + s.id,
|
|
|
|
Address: net2.HostPort(addr, port),
|
|
|
|
}
|
|
|
|
|
|
|
|
s.regSvc = ®istry.Service{
|
|
|
|
Name: s.opts.name,
|
|
|
|
Version: s.opts.version,
|
|
|
|
Nodes: []*registry.Node{node},
|
|
|
|
}
|
|
|
|
|
|
|
|
// register the service
|
|
|
|
if err := regFunc(s.regSvc); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-22 14:02:06 +01:00
|
|
|
func (s *service) run() error {
|
|
|
|
s.mu.Lock()
|
2020-11-08 19:28:33 +01:00
|
|
|
s.closed = make(chan struct{})
|
2020-01-22 14:02:06 +01:00
|
|
|
for i := range s.opts.beforeStart {
|
|
|
|
if err := s.opts.beforeStart[i](); err != nil {
|
2020-11-08 19:28:33 +01:00
|
|
|
s.mu.Unlock()
|
2020-01-22 14:02:06 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.opts.address = s.list.Addr().String()
|
2020-11-08 19:28:33 +01:00
|
|
|
|
|
|
|
if err := s.register(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.running = true
|
2020-01-22 14:02:06 +01:00
|
|
|
errs := make(chan error)
|
|
|
|
go func() {
|
|
|
|
errs <- s.server.Serve(s.list)
|
|
|
|
}()
|
|
|
|
for i := range s.opts.afterStart {
|
|
|
|
if err := s.opts.afterStart[i](); err != nil {
|
|
|
|
s.mu.Unlock()
|
|
|
|
s.Stop()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.mu.Unlock()
|
2020-11-08 19:28:33 +01:00
|
|
|
if err := <-errs; err != nil{
|
|
|
|
logrus.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2020-01-22 14:02:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Start() error {
|
|
|
|
return s.cmd.Execute()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Stop() error {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2020-08-07 10:59:27 +02:00
|
|
|
if !s.running {
|
2020-01-22 14:02:06 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for i := range s.opts.beforeStop {
|
|
|
|
if err := s.opts.beforeStop[i](); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-11-15 15:51:29 +01:00
|
|
|
if err := s.opts.registry.Deregister(s.regSvc); err != nil {
|
|
|
|
logrus.Errorf("failed to deregister service: %v", err)
|
|
|
|
}
|
|
|
|
defer close(s.closed)
|
2020-01-22 14:02:06 +01:00
|
|
|
s.server.GracefulStop()
|
|
|
|
s.running = false
|
2020-11-15 15:51:29 +01:00
|
|
|
s.cancel()
|
2020-01-22 14:02:06 +01:00
|
|
|
for i := range s.opts.afterStop {
|
|
|
|
if err := s.opts.afterStop[i](); err != nil {
|
2020-11-08 19:28:33 +01:00
|
|
|
return err
|
2020-01-22 14:02:06 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-08 19:28:33 +01:00
|
|
|
logrus.Info("server stopped")
|
2020-01-22 14:02:06 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) Close() error {
|
2020-03-03 14:54:53 +01:00
|
|
|
err := multierr.Combine(s.Stop())
|
|
|
|
if s.opts.db != nil {
|
|
|
|
err = multierr.Append(s.opts.db.Close(), err)
|
|
|
|
}
|
2020-11-08 19:28:33 +01:00
|
|
|
<-s.closed
|
2020-03-03 14:54:53 +01:00
|
|
|
return err
|
2020-01-22 14:02:06 +01:00
|
|
|
}
|