github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/service.go (about)

     1  package service
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/signal"
     9  	"runtime"
    10  	"syscall"
    11  
    12  	"github.com/urfave/cli/v2"
    13  
    14  	"github.com/tickoalcantara12/micro/v3/cmd"
    15  	"github.com/tickoalcantara12/micro/v3/service/client"
    16  	mudebug "github.com/tickoalcantara12/micro/v3/service/debug"
    17  	debug "github.com/tickoalcantara12/micro/v3/service/debug/handler"
    18  	"github.com/tickoalcantara12/micro/v3/service/logger"
    19  	"github.com/tickoalcantara12/micro/v3/service/model"
    20  	"github.com/tickoalcantara12/micro/v3/service/server"
    21  )
    22  
    23  var (
    24  	// errMissingName is returned by service.Run when a service is run
    25  	// prior to it's name being set.
    26  	errMissingName = errors.New("missing service name")
    27  )
    28  
    29  // Service is a Micro Service which honours the go-micro/service interface
    30  type Service struct {
    31  	opts Options
    32  }
    33  
    34  // Run the default service and waits for it to exist
    35  func Run() {
    36  	// setup a new service, calling New() will trigger the cmd package
    37  	// to parse the command line and
    38  	srv := New()
    39  
    40  	if err := srv.Run(); err == errMissingName {
    41  		fmt.Println("Micro services must be run using \"micro run\"")
    42  		os.Exit(1)
    43  	} else if err != nil {
    44  		logger.Fatalf("Error running %v service: %v", srv.Name(), err)
    45  	}
    46  }
    47  
    48  // New returns a new Micro Service
    49  func New(opts ...Option) *Service {
    50  	// before extracts service options from the CLI flags. These
    51  	// aren't set by the cmd package to prevent a circular dependancy.
    52  	// prepend them to the array so options passed by the user to this
    53  	// function are applied after (taking precedence)
    54  	before := func(ctx *cli.Context) error {
    55  		if n := ctx.String("service_name"); len(n) > 0 {
    56  			opts = append(opts, Name(n))
    57  		}
    58  		if v := ctx.String("service_version"); len(v) > 0 {
    59  			opts = append(opts, Version(v))
    60  		}
    61  		// service address injected by the runtime takes priority as the service port must match the
    62  		// port the server is running on
    63  		if a := ctx.String("service_address"); len(a) > 0 {
    64  			opts = append(opts, Address(a))
    65  		}
    66  		return nil
    67  	}
    68  
    69  	// setup micro, this triggers the Before
    70  	// function which parses CLI flags.
    71  	cmd.New(cmd.SetupOnly(), cmd.Before(before)).Run()
    72  
    73  	// return a new service
    74  	return &Service{opts: newOptions(opts...)}
    75  }
    76  
    77  // Name of the service
    78  func (s *Service) Name() string {
    79  	return s.opts.Name
    80  }
    81  
    82  // Version of the service
    83  func (s *Service) Version() string {
    84  	return s.opts.Version
    85  }
    86  
    87  // Handle registers a handler
    88  func (s *Service) Handle(v interface{}) error {
    89  	return s.Server().Handle(s.Server().NewHandler(v))
    90  }
    91  
    92  // Subscribe registers a subscriber
    93  func (s *Service) Subscribe(topic string, v interface{}) error {
    94  	return s.Server().Subscribe(s.Server().NewSubscriber(topic, v))
    95  }
    96  
    97  func (s *Service) Init(opts ...Option) {
    98  	for _, o := range opts {
    99  		o(&s.opts)
   100  	}
   101  }
   102  
   103  func (s *Service) Options() Options {
   104  	return s.opts
   105  }
   106  
   107  func (s *Service) Client() client.Client {
   108  	return client.DefaultClient
   109  }
   110  
   111  func (s *Service) Model() model.Model {
   112  	return model.DefaultModel
   113  }
   114  
   115  func (s *Service) Server() server.Server {
   116  	return server.DefaultServer
   117  }
   118  
   119  func (s *Service) String() string {
   120  	return "micro"
   121  }
   122  
   123  func (s *Service) Start() error {
   124  	for _, fn := range s.opts.BeforeStart {
   125  		if err := fn(); err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	if err := s.Server().Start(); err != nil {
   131  		return err
   132  	}
   133  
   134  	for _, fn := range s.opts.AfterStart {
   135  		if err := fn(); err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  func (s *Service) Stop() error {
   144  	var gerr error
   145  
   146  	for _, fn := range s.opts.BeforeStop {
   147  		if err := fn(); err != nil {
   148  			gerr = err
   149  		}
   150  	}
   151  
   152  	if err := server.DefaultServer.Stop(); err != nil {
   153  		return err
   154  	}
   155  
   156  	for _, fn := range s.opts.AfterStop {
   157  		if err := fn(); err != nil {
   158  			gerr = err
   159  		}
   160  	}
   161  
   162  	return gerr
   163  }
   164  
   165  // Run the service
   166  func (s *Service) Run() error {
   167  	// ensure service's have a name, this is injected by the runtime manager
   168  	if len(s.Name()) == 0 {
   169  		return errMissingName
   170  	}
   171  
   172  	// register the debug handler
   173  	s.Server().Handle(
   174  		s.Server().NewHandler(
   175  			debug.NewHandler(),
   176  			server.InternalHandler(true),
   177  		),
   178  	)
   179  
   180  	// start the profiler
   181  	if mudebug.DefaultProfiler != nil {
   182  		// to view mutex contention
   183  		runtime.SetMutexProfileFraction(5)
   184  		// to view blocking profile
   185  		runtime.SetBlockProfileRate(1)
   186  
   187  		if err := mudebug.DefaultProfiler.Start(); err != nil {
   188  			return err
   189  		}
   190  
   191  		defer mudebug.DefaultProfiler.Stop()
   192  	}
   193  
   194  	if logger.V(logger.InfoLevel, logger.DefaultLogger) {
   195  		logger.Infof("Starting [service] %s", s.Name())
   196  	}
   197  
   198  	if err := s.Start(); err != nil {
   199  		return err
   200  	}
   201  
   202  	ch := make(chan os.Signal, 1)
   203  	if s.opts.Signal {
   204  		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL)
   205  	}
   206  
   207  	// wait on kill signal
   208  	<-ch
   209  	return s.Stop()
   210  }
   211  
   212  // Handle is syntactic sugar for registering a handler
   213  func Handle(h interface{}, opts ...server.HandlerOption) error {
   214  	return server.DefaultServer.Handle(server.DefaultServer.NewHandler(h, opts...))
   215  }
   216  
   217  // Subscribe is syntactic sugar for registering a subscriber
   218  func Subscribe(topic string, h interface{}, opts ...server.SubscriberOption) error {
   219  	return server.DefaultServer.Subscribe(server.DefaultServer.NewSubscriber(topic, h, opts...))
   220  }
   221  
   222  // Event is an object messages are published to
   223  type Event struct {
   224  	topic string
   225  }
   226  
   227  // Publish a message to an event
   228  func (e *Event) Publish(ctx context.Context, msg interface{}) error {
   229  	return client.Publish(ctx, client.NewMessage(e.topic, msg))
   230  }
   231  
   232  // NewEvent creates a new event publisher
   233  func NewEvent(topic string) *Event {
   234  	return &Event{topic}
   235  }