Based on go micro version 2.9.1,
Example code: example/greeter, git commit:3b3de68cded8879ca3dde5d81192f2881619aabd
The core of a microservice server has only three steps
service := micro.NewService() service.Init() service.Run()
First look at micro NewService()
service := micro.NewService( micro.Name("greeter"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), )
micro. The parameters of newservice will return an Option,
These parameters do nothing but return some functions for setting,
This writing method is "function option mode", which can be used for reference https://segmentfault.com/a/11...
First look at what is done in NewService()
// Name of the service func Name(n string) Option { return func(o *Options) { o.Server.Init(server.Name(n)) } } //Option function in micro Defined in go //The Options structure is in Options Defined in go type Option func(*Options) // Options for micro service type Options struct { Auth auth.Auth Broker broker.Broker Cmd cmd.Cmd Config config.Config Client client.Client Server server.Server Store store.Store Registry registry.Registry Router router.Router Runtime runtime.Runtime Transport transport.Transport Profile profile.Profile // Before and After funcs BeforeStart []func() error BeforeStop []func() error AfterStart []func() error AfterStop []func() error // Other options for implementations of the interface // can be stored in a context Context context.Context Signal bool }
server.Name(n) implements this by setting micro through the functions provided by the micro package options
// Server name func Name(n string) Option { return func(o *Options) { o.Name = n } }
Service is called in NewService Newservice in go (opts...)
// NewService creates and returns a new Service based on the packages within. func NewService(opts ...Option) Service { return newService(opts...) }
type service struct { opts Options once sync.Once } func newService(opts ...Option) Service { service := new(service) options := newOptions(opts...) // service name serviceName := options.Server.Options().Name // we pass functions to the wrappers since the values can change during initialisation authFn := func() auth.Auth { return options.Server.Options().Auth } cacheFn := func() *client.Cache { return options.Client.Options().Cache } // wrap client to inject From-Service header on any calls options.Client = wrapper.FromService(serviceName, options.Client) options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client) options.Client = wrapper.CacheClient(cacheFn, options.Client) options.Client = wrapper.AuthClient(authFn, options.Client) // wrap the server to provide handler stats options.Server.Init( server.WrapHandler(wrapper.HandlerStats(stats.DefaultStats)), server.WrapHandler(wrapper.TraceHandler(trace.DefaultTracer)), server.WrapHandler(wrapper.AuthHandler(authFn)), ) // set opts service.opts = options return service } func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt }
-
Instantiate the service structure, initialize the parameters and assign them to opts
- First initialize some Options properties
- Then traverse and execute micro from opts The parameter of newservice (actually a function) sets various values
-
Is options Several wrapper s are added to the client, and four key value pairs are added to ctx, which will be played back to the header when the client initiates the request
- wrapper. In fromservice(), ctx add micro from service
- wrapper. TraceHandler(trace.DefaultTracer) -> t.Start(ctx, req.Service()+"."+ req. Endpoint()) -> Tracer. Start() (trace / memory / memory. Go), CTX add micro trace ID and micro span ID
- server.WrapHandler(wrapper.AuthHandler(authFn)), ctx add micro namespace
- Is options Add several wrapper s to the server
options.Server.Init(server.Name(n))
Here are the options Where does the server come from? There is no place to initialize this attribute. In fact, it is in go micro / defaults init() in go
func init() { // default client client.DefaultClient = gcli.NewClient() // default server server.DefaultServer = gsrv.NewServer() // default store store.DefaultStore = memoryStore.NewStore() // set default trace trace.DefaultTracer = memTrace.NewTracer() }
init() defines four variables: server, client, store and trace. It should be noted that the server here is the default grpc, which is an internal variable of the micro package and cannot be accessed directly elsewhere
o.Server. init() of init (server.Name(n)) is server / grpc init() in go initializes grpcserver Some properties of opts [type is server.Options], such as server Name (n) is set to grpcserver opts. Name
grpc. Other parts of configure () will not be examined here for the time being
func (g *grpcServer) Init(opts ...server.Option) error { g.configure(opts...) return nil } func (g *grpcServer) configure(opts ...server.Option) { g.Lock() defer g.Unlock() // Don't reprocess where there's no config if len(opts) == 0 && g.srv != nil { return } for _, o := range opts { o(&g.opts) } maxMsgSize := g.getMaxMsgSize() gopts := []grpc.ServerOption{ grpc.MaxRecvMsgSize(maxMsgSize), grpc.MaxSendMsgSize(maxMsgSize), grpc.UnknownServiceHandler(g.handler), } if creds := g.getCredentials(); creds != nil { gopts = append(gopts, grpc.Creds(creds)) } if opts := g.getGrpcOptions(); opts != nil { gopts = append(gopts, opts...) } g.rsvc = nil g.srv = grpc.NewServer(gopts...) }
Let's look at service Init()
// Init will parse the command line flags. Any flags set will // override the above settings. Options defined here will // override anything set on the command line. service.Init( // Add runtime action // We could actually do this above micro.Action(func(c *cli.Context) error { if c.Bool("run_client") { runClient(service) os.Exit(0) } return nil }), )
// Init initialises options. Additionally it calls cmd.Init // which parses command line flags. cmd.Init is only called // on first Init. func (s *service) Init(opts ...Option) { // process options for _, o := range opts { o(&s.opts) } s.once.Do(func() { // setup the plugins for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") { if len(p) == 0 { continue } // load the plugin c, err := plugin.Load(p) if err != nil { logger.Fatal(err) } // initialise the plugin if err := plugin.Init(c); err != nil { logger.Fatal(err) } } // set cmd name if len(s.opts.Cmd.App().Name) == 0 { s.opts.Cmd.App().Name = s.Server().Options().Name } // Initialise the command flags, overriding new service if err := s.opts.Cmd.Init( cmd.Auth(&s.opts.Auth), cmd.Broker(&s.opts.Broker), cmd.Registry(&s.opts.Registry), cmd.Runtime(&s.opts.Runtime), cmd.Transport(&s.opts.Transport), cmd.Client(&s.opts.Client), cmd.Config(&s.opts.Config), cmd.Server(&s.opts.Server), cmd.Store(&s.opts.Store), cmd.Profile(&s.opts.Profile), ); err != nil { logger.Fatal(err) } // Explicitly set the table name to the service name name := s.opts.Cmd.App().Name s.opts.Store.Init(store.Table(name)) }) }
- And micro The parameter processing of newservice is the same as that of initialization parameters
-
s.once.Do() is executed only once
- Loading plug-ins
- Set cmd name
- Initialize the command line parameters and override the properties in the service
- Explicitly set the cmd name to the service name
The last step is service Run()
func (s *service) Run() error { // register the debug handler s.opts.Server.Handle( s.opts.Server.NewHandler( handler.NewHandler(s.opts.Client), server.InternalHandler(true), ), ) // start the profiler if s.opts.Profile != nil { // to view mutex contention rtime.SetMutexProfileFraction(5) // to view blocking profile rtime.SetBlockProfileRate(1) if err := s.opts.Profile.Start(); err != nil { return err } defer s.opts.Profile.Stop() } if logger.V(logger.InfoLevel, logger.DefaultLogger) { logger.Infof("Starting [service] %s", s.Name()) } if err := s.Start(); err != nil { return err } ch := make(chan os.Signal, 1) if s.opts.Signal { signal.Notify(ch, signalutil.Shutdown()...) } select { // wait on kill signal case <-ch: // wait on context cancel case <-s.opts.Context.Done(): } return s.Stop() }
- Register debug handler
- profiler, start console, output information
- s.start(), see later
- Listen for exit Signal and ctx cancel Signal, and execute s.stop() after receiving the Signal
func (s *service) Start() error { for _, fn := range s.opts.BeforeStart { if err := fn(); err != nil { return err } } if err := s.opts.Server.Start(); err != nil { return err } for _, fn := range s.opts.AfterStart { if err := fn(); err != nil { return err } } return nil } func (s *service) Stop() error { var gerr error for _, fn := range s.opts.BeforeStop { if err := fn(); err != nil { gerr = err } } if err := s.opts.Server.Stop(); err != nil { return err } for _, fn := range s.opts.AfterStop { if err := fn(); err != nil { gerr = err } } return gerr }
Start:
- Execute s.opts Functions in the beforestart list
- Start service s.opts Server. Start (), depending on the service used
- Execute s.opts Functions in the afterstart list
sign out:
The exit process is the same as the start process, and execute s.opts BeforeStop,s.opts.Server.Stop(),s.opts.AfterStop
The example of BeforeStart is similar to others
func aa() error { fmt.Println("beforestart fmt") return nil } service := micro.NewService( micro.BeforeStart(aa), )
Default store Defaultstore usage https://github.com/patrickmn/...
In memory / memory Some encapsulation is done in go
Other init() packages referenced in golang will automatically execute init()
logger/default.go initialize logger
micro. See go micro / options for all setting options in newservice() Go, see [micro in action (II): project structure and startup process]
https://medium.com/@dche423/m...
This is the start-up process of go micro microservice. You must understand the function option mode before you can understand go micro.