基於go-micro 2.9.1版本,git
樣例代碼example/greeter,git commit:3b3de68cded8879ca3dde5d81192f2881619aabdgithub
一個微服務server的核心只有3步golang
service := micro.NewService() service.Init() service.Run()
先看micro.NewService()web
service := micro.NewService( micro.Name("greeter"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), )
micro.NewService的參數都會返回一個Option
,
這些參數沒有作任何事情,只是返回了一些設置用的函數,segmentfault
這種寫法是「函數選項模式」,可參考https://segmentfault.com/a/11...app
先看NewService()
裏面作了什麼ide
// 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 }
實例化service結構體,初始化參數並賦值到optsthis
爲options.Client增長几個wrapper,ctx中增長了4個鍵值對,在client發起請求的時候回放到header中
Micro-From-Service
Micro-Trace-Id, Micro-Span-Id
Micro-Namespace
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)) }) }
s.once.Do(),只執行一次
最後一步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() }
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 }
啓動:
s.opts.BeforeStart
列表中的函數s.opts.Server.Start()
,具體就看用的什麼服務了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。
go micro 分析系列文章
go micro server 啓動分析
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 插件consul
go micro plugin
go micro jwt 網關鑑權
go micro 鏈路追蹤
go micro 熔斷與限流
go micro wrapper 中間件
go micro metrics 接入Prometheus、Grafana