Analysis of go micro server startup

Time:2021-4-3

Based on go micro version 2.9.1,

Example / greeter, GIT commit:3b3de68cded8879ca3dde5d81192f2881619aabd

The core of a microservice server has only three steps

service := micro.NewService()
service.Init()
service.Run()

Look first micro.NewService ()

service := micro.NewService(
    micro.Name("greeter"),
    micro.Version("latest"),
    micro.Metadata(map[string]string{
        "type": "helloworld",
    }),
)

micro.NewService Will return aOption,
These parameters do nothing but return some functions for setting,

This writing method is “function option mode” https://segmentfault.com/a/11…

Look firstNewService()What’s in it

// Name of the service
func Name(n string) Option {
    return func(o *Options) {
        o.Server.Init(server.Name(n))
    }
}

//Option function in micro.go Defined in
//Where are the options structures options.go Defined in
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) To achieve this, set the function provided by the micro package micro.options

// Server name
func Name(n string) Option {
    return func(o *Options) {
        o.Name = n
    }
}

Called in NewService service.go Newservice (opts…) in

// 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
}
  1. Instantiate the service structure, initialize the parameters and assign them to opts

    1. Initialize some options properties first
    2. And then traverse the execution from the opts micro.NewService Parameter (actually function), set various values
  2. by options.Client Add several wrappers, and add four key value pairs in CTX to play back to the header when the client initiates the request

    1. wrapper.FromService In (), CTX increasedMicro-From-Service
    2. wrapper.TraceHandler ( trace.DefaultTracer ) -> t.Start(ctx, req.Service ()+”.”+ req.Endpoint ()) -> Tracer.start ()(trace/memory/ memory.go )CTX increasedMicro-Trace-Id, Micro-Span-Id
    3. server.WrapHandler ( wrapper.AuthHandler (authfn)), CTX increasedMicro-Namespace
  3. by options.Server Add several wrappers
options.Server.Init(server.Name(n))

there options.Server Where did it come from? I haven’t seen the initialization of this property before. In fact, it’s in go micro/ defaults.go In init()

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,traceIt 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 in other places

o. Server.Init ( server.Name (n) ) ofInit()Server/ grpc.go Init() in grpcServer.opts [type is server.Options ]Some properties of, such as server.Name (n) The setting is grpcServer.opts.Name

grpc.configure The rest of () is not detailed here

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 it nextservice.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))
    })
}
  1. and micro.NewService The initialization parameter is the same as the initialization parameter
  2. s. once.Do (), only once

    1. Loading plug-ins
    2. Set CMD name
    3. Initialize the command line parameters and override the properties in the service
    4. Explicitly set the CMD name to the service name

The last stepservice.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()
}
  1. Register debug handler
  2. Start the profiler and the console outputs the startup information
  3. s. Let’s see later
  4. Monitor the exit signal and CTX cancel signal, and execute s.stop ()
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 up:

  1. In turns.opts.BeforeStartFunctions in the list
  2. Start the services.opts.Server.Start()It depends on the service
  3. In turns.opts.AfterStartFunctions in the list

sign out:
The exit process is the same as the start process, which is executed in turns.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),
)

defaultstore.DefaultStoreuse https://github.com/patrickmn/…
In memory/ memory.go Some encapsulation is done in

otherinit(), packages referenced in golang will automatically execute init ()
logger/ default.go Initialize logger

micro.NewService For all the setting options in (), see go micro/ options.go For details, please refer to micro in action (2): project structure and startup process
https://medium.com/@dche423/m…

This is the start-up process of go micro services. You must understand it firstFunction option modeTo help you understand go micro.

Go micro analysis series
Analysis of go micro server startup
go micro client
go micro broker
go micro cmd
go micro config
go micro store
go micro registry
go micro router
go micro runtime
go micro transport
go micro web
Go micro registry plug-in consult
go micro plugin
Go micro JWT gateway authentication
Go micro link tracking
Go micro fusing and current limiting
Go micro wrapper Middleware
Go micro metrics access to Prometheus and grafana

Recommended Today

Build HTTP service with C + + Mongoose

Mongoose source code address:https://github.com/cesanta/mo… Mongoose user manual:https://www.cesanta.com/devel… Mngoose set up HTTP service #include <string> #include “mongoose.h” using namespace std; static const char *s_http_port = “8000”; static void ev_handler(mg_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; if (ev == MG_EV_HTTP_REQUEST) { std::string uri; if (hm->uri.p && hm->uri.p[0] == ‘/’) […]