乐趣区

关于micro:go-micro-server-启动分析

基于 go-micro 2.9.1 版本,

样例代码 example/greeter,git commit:3b3de68cded8879ca3dde5d81192f2881619aabd

一个微服务 server 的外围只有 3 步

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

先看 micro.NewService()

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

micro.NewService 的参数都会返回一个 Option,
这些参数没有做任何事件,只是返回了一些设置用的函数,

这种写法是“函数选项模式”,可参考 https://segmentfault.com/a/11…

先看 NewService() 外面做了什么

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

//Option 函数在 micro.go 中定义
//Options 构造体在 options.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)实现这样, 通过 micro 包提供的函数设置 micro.options

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

NewService 中调用了 service.go 中的 newService(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
}
  1. 实例化 service 构造体,初始化参数并赋值到 opts

    1. 先初始化一些 Options 属性
    2. 再从 opts 中遍历执行 micro.NewService 的参数(理论就是函数),设置各种值
  2. 为 options.Client 减少几个 wrapper,ctx 中减少了 4 个键值对,在 client 发动申请的时候回放到 header 中

    1. wrapper.FromService()中,ctx 减少Micro-From-Service
    2. wrapper.TraceHandler(trace.DefaultTracer) -> t.Start(ctx, req.Service()+”.”+req.Endpoint()) -> Tracer.start()(trace/memory/memory.go),ctx 减少Micro-Trace-Id, Micro-Span-Id
    3. server.WrapHandler(wrapper.AuthHandler(authFn)), ctx 减少Micro-Namespace
  3. 为 options.Server 减少几个 wrapper
options.Server.Init(server.Name(n))

这里的 options.Server 是哪里来的呢, 后面没见有初始化这个属性的中央, 其实在 go-micro/defaults.go 的 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()中定义了 4 个变量,server,client,store,trace,须要留神的是这里的 server 是默认的 grpc,是 micro 包外部变量,在其余中央无奈间接拜访

o.Server.Init(server.Name(n)) 的 Init() 则是 server/grpc.go 中的 init(), 初始化 grpcServer.opts[类型是 server.Options]的一些属性,如 server.Name(n)设置的是 grpcServer.opts.Name

grpc.configure()中的其余局部这里暂不细看

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...)
}

上面再看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))
    })
}
  1. 和 micro.NewService 的参数解决一样,初始化参数
  2. s.once.Do(),只执行一次

    1. 加载插件
    2. 设置 cmd 名字
    3. 初始化命令行参数,笼罩 service 中的属性
    4. 显式地将 cmd 名字设置为服务名

最初一步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()}
  1. 注册 debug handler
  2. 启动 profiler,控制台输入启动信息
  3. s.start(), 前面再看
  4. 监听退出 Signal 和 ctx 勾销信号,收到信号后执行 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
}

启动:

  1. 顺次执行 s.opts.BeforeStart 列表中的函数
  2. 启动服务s.opts.Server.Start(),具体就看用的什么服务了
  3. 顺次执行 s.opts.AfterStart 列表中的函数

退出:
退出流程与启动流程统一,顺次执行s.opts.BeforeStop,s.opts.Server.Stop(),s.opts.AfterStop

BeforeStart 的例子,其余的相似

func aa() error {fmt.Println("beforestart fmt")
    return nil
}

service := micro.NewService(micro.BeforeStart(aa),
)

默认的 store.DefaultStore 应用 https://github.com/patrickmn/…
在 memory/memory.go 中做了一些封装

其余init(),在 golang 中援用的包,会主动执行 init()
logger/default.go 初始化 logger

micro.NewService()中的所有设置选项见 go-micro/options.go,能够参见【Micro In Action(二):我的项目构造与启动过程】
https://medium.com/@dche423/m…

这就是 go micro 微服务的启动过程,肯定要先理解 函数选项模式 才有助于了解 go micro。

退出移动版