github.com/micro/go-micro/v2@v2.9.1/service.go (about)

     1  package micro
     2  
     3  import (
     4  	"os"
     5  	"os/signal"
     6  	rtime "runtime"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/micro/go-micro/v2/auth"
    11  	"github.com/micro/go-micro/v2/client"
    12  	"github.com/micro/go-micro/v2/config/cmd"
    13  	"github.com/micro/go-micro/v2/debug/service/handler"
    14  	"github.com/micro/go-micro/v2/debug/stats"
    15  	"github.com/micro/go-micro/v2/debug/trace"
    16  	"github.com/micro/go-micro/v2/logger"
    17  	"github.com/micro/go-micro/v2/plugin"
    18  	"github.com/micro/go-micro/v2/server"
    19  	"github.com/micro/go-micro/v2/store"
    20  	signalutil "github.com/micro/go-micro/v2/util/signal"
    21  	"github.com/micro/go-micro/v2/util/wrapper"
    22  )
    23  
    24  type service struct {
    25  	opts Options
    26  
    27  	once sync.Once
    28  }
    29  
    30  func newService(opts ...Option) Service {
    31  	service := new(service)
    32  	options := newOptions(opts...)
    33  
    34  	// service name
    35  	serviceName := options.Server.Options().Name
    36  
    37  	// we pass functions to the wrappers since the values can change during initialisation
    38  	authFn := func() auth.Auth { return options.Server.Options().Auth }
    39  	cacheFn := func() *client.Cache { return options.Client.Options().Cache }
    40  
    41  	// wrap client to inject From-Service header on any calls
    42  	options.Client = wrapper.FromService(serviceName, options.Client)
    43  	options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client)
    44  	options.Client = wrapper.CacheClient(cacheFn, options.Client)
    45  	options.Client = wrapper.AuthClient(authFn, options.Client)
    46  
    47  	// wrap the server to provide handler stats
    48  	options.Server.Init(
    49  		server.WrapHandler(wrapper.HandlerStats(stats.DefaultStats)),
    50  		server.WrapHandler(wrapper.TraceHandler(trace.DefaultTracer)),
    51  		server.WrapHandler(wrapper.AuthHandler(authFn)),
    52  	)
    53  
    54  	// set opts
    55  	service.opts = options
    56  
    57  	return service
    58  }
    59  
    60  func (s *service) Name() string {
    61  	return s.opts.Server.Options().Name
    62  }
    63  
    64  // Init initialises options. Additionally it calls cmd.Init
    65  // which parses command line flags. cmd.Init is only called
    66  // on first Init.
    67  func (s *service) Init(opts ...Option) {
    68  	// process options
    69  	for _, o := range opts {
    70  		o(&s.opts)
    71  	}
    72  
    73  	s.once.Do(func() {
    74  		// setup the plugins
    75  		for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") {
    76  			if len(p) == 0 {
    77  				continue
    78  			}
    79  
    80  			// load the plugin
    81  			c, err := plugin.Load(p)
    82  			if err != nil {
    83  				logger.Fatal(err)
    84  			}
    85  
    86  			// initialise the plugin
    87  			if err := plugin.Init(c); err != nil {
    88  				logger.Fatal(err)
    89  			}
    90  		}
    91  
    92  		// set cmd name
    93  		if len(s.opts.Cmd.App().Name) == 0 {
    94  			s.opts.Cmd.App().Name = s.Server().Options().Name
    95  		}
    96  
    97  		// Initialise the command flags, overriding new service
    98  		if err := s.opts.Cmd.Init(
    99  			cmd.Auth(&s.opts.Auth),
   100  			cmd.Broker(&s.opts.Broker),
   101  			cmd.Registry(&s.opts.Registry),
   102  			cmd.Runtime(&s.opts.Runtime),
   103  			cmd.Transport(&s.opts.Transport),
   104  			cmd.Client(&s.opts.Client),
   105  			cmd.Config(&s.opts.Config),
   106  			cmd.Server(&s.opts.Server),
   107  			cmd.Store(&s.opts.Store),
   108  			cmd.Profile(&s.opts.Profile),
   109  		); err != nil {
   110  			logger.Fatal(err)
   111  		}
   112  
   113  		// Explicitly set the table name to the service name
   114  		name := s.opts.Cmd.App().Name
   115  		s.opts.Store.Init(store.Table(name))
   116  	})
   117  }
   118  
   119  func (s *service) Options() Options {
   120  	return s.opts
   121  }
   122  
   123  func (s *service) Client() client.Client {
   124  	return s.opts.Client
   125  }
   126  
   127  func (s *service) Server() server.Server {
   128  	return s.opts.Server
   129  }
   130  
   131  func (s *service) String() string {
   132  	return "micro"
   133  }
   134  
   135  func (s *service) Start() error {
   136  	for _, fn := range s.opts.BeforeStart {
   137  		if err := fn(); err != nil {
   138  			return err
   139  		}
   140  	}
   141  
   142  	if err := s.opts.Server.Start(); err != nil {
   143  		return err
   144  	}
   145  
   146  	for _, fn := range s.opts.AfterStart {
   147  		if err := fn(); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func (s *service) Stop() error {
   156  	var gerr error
   157  
   158  	for _, fn := range s.opts.BeforeStop {
   159  		if err := fn(); err != nil {
   160  			gerr = err
   161  		}
   162  	}
   163  
   164  	if err := s.opts.Server.Stop(); err != nil {
   165  		return err
   166  	}
   167  
   168  	for _, fn := range s.opts.AfterStop {
   169  		if err := fn(); err != nil {
   170  			gerr = err
   171  		}
   172  	}
   173  
   174  	return gerr
   175  }
   176  
   177  func (s *service) Run() error {
   178  	// register the debug handler
   179  	s.opts.Server.Handle(
   180  		s.opts.Server.NewHandler(
   181  			handler.NewHandler(s.opts.Client),
   182  			server.InternalHandler(true),
   183  		),
   184  	)
   185  
   186  	// start the profiler
   187  	if s.opts.Profile != nil {
   188  		// to view mutex contention
   189  		rtime.SetMutexProfileFraction(5)
   190  		// to view blocking profile
   191  		rtime.SetBlockProfileRate(1)
   192  
   193  		if err := s.opts.Profile.Start(); err != nil {
   194  			return err
   195  		}
   196  		defer s.opts.Profile.Stop()
   197  	}
   198  
   199  	if logger.V(logger.InfoLevel, logger.DefaultLogger) {
   200  		logger.Infof("Starting [service] %s", s.Name())
   201  	}
   202  
   203  	if err := s.Start(); err != nil {
   204  		return err
   205  	}
   206  
   207  	ch := make(chan os.Signal, 1)
   208  	if s.opts.Signal {
   209  		signal.Notify(ch, signalutil.Shutdown()...)
   210  	}
   211  
   212  	select {
   213  	// wait on kill signal
   214  	case <-ch:
   215  	// wait on context cancel
   216  	case <-s.opts.Context.Done():
   217  	}
   218  
   219  	return s.Stop()
   220  }