examples/web 有一個web的例子,這裏比較簡單html
service.HandleFunc("/", helloWorldHandler)
node
這一行指定處理程序比較簡單,第2個參數定義了一個函數,只要知足條件就行web
handler func(http.ResponseWriter, *http.Request)
segmentfault
實際項目中不太可能只用go micro, 從0開始手擼全部其餘輪子,那麼可不能夠在go micro中引入經常使用的框架呢?api
固然能夠,來看一個引入gin的例子examples/greeter/api/gin/gin.goapp
func main() { // Create service service := web.NewService( web.Name("go.micro.api.greeter"), ) service.Init() // setup Greeter Server Client cl = hello.NewSayService("go.micro.srv.greeter", client.DefaultClient) // Create RESTful handler (using Gin) say := new(Say) router := gin.Default() router.GET("/greeter", say.Anything) router.GET("/greeter/:name", say.Hello) // Register Handler service.Handle("/", router) // Run server if err := service.Run(); err != nil { log.Fatal(err) } }
關鍵是service.Handle("/", router)
框架
這個router是gin.Engine, service.Handle()的第二個參數是handler http.Handler
tcp
type Handler interface { ServeHTTP(ResponseWriter, \*Request) }
也就是gin.Engine中只要實現了ServeHTTP()就能夠知足條件,來看下gin的ServeHTTP()函數
// ServeHTTP conforms to the http.Handler interface. func (engine \*Engine) ServeHTTP(w http.ResponseWriter, req \*http.Request) { c := engine.pool.Get().(\*Context) c.writermem.reset(w) c.Request = req c.reset() engine.handleHTTPRequest(c) engine.pool.Put(c) }
這樣就能夠使用gin框架完成業務代碼了,其餘框架都相似,examples/greeter/api 目錄下有beego、graphql、rest、rpc等例子oop
下面看看web的整個啓動流程
以examples/web爲例
func main() { service := web.NewService( web.Name("go.micro.web.greeter"), ) service.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { r.ParseForm() name := r.Form.Get("name") if len(name) == 0 { name = "World" } cl := hello.NewSayService("go.micro.srv.greeter", client.DefaultClient) rsp, err := cl.Hello(context.Background(), &hello.Request{ Name: name, }) if err != nil { http.Error(w, err.Error(), 500) return } w.Write([]byte(`<html><body><h1>` + rsp.Msg + `</h1></body></html>`)) return } fmt.Fprint(w, `<html><body><h1>Enter Name<h1><form method=post><input name=name type=text /></form></body></html>`) }) if err := service.Init(); err != nil { log.Fatal(err) } if err := service.Run(); err != nil { log.Fatal(err) } }
代碼結構和普通service同樣,只是micro變成了web
先看web.NewService()
// NewService returns a new web.Service func NewService(opts ...Option) Service { return newService(opts...) } func newService(opts ...Option) Service { options := newOptions(opts...) s := &service{ opts: options, mux: http.NewServeMux(), static: true, } s.srv = s.genSrv() return s } func newOptions(opts ...Option) Options { opt := Options{ Name: DefaultName, Version: DefaultVersion, Id: DefaultId, Address: DefaultAddress, RegisterTTL: DefaultRegisterTTL, RegisterInterval: DefaultRegisterInterval, StaticDir: DefaultStaticDir, Service: micro.NewService(), Context: context.TODO(), Signal: true, } for _, o := range opts { o(&opt) } if opt.RegisterCheck == nil { opt.RegisterCheck = DefaultRegisterCheck } return opt }
作了如下事情
s.genSrv()生成registry.Service{}
再來看service.Init()
func (s *service) Init(opts ...Option) error { s.Lock() for _, o := range opts { o(&s.opts) } serviceOpts := []micro.Option{} if len(s.opts.Flags) > 0 { serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...)) } if s.opts.Registry != nil { serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry)) } s.Unlock() serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error { s.Lock() defer s.Unlock() if ttl := ctx.Int("register_ttl"); ttl > 0 { s.opts.RegisterTTL = time.Duration(ttl) * time.Second } if interval := ctx.Int("register_interval"); interval > 0 { s.opts.RegisterInterval = time.Duration(interval) * time.Second } if name := ctx.String("server_name"); len(name) > 0 { s.opts.Name = name } if ver := ctx.String("server_version"); len(ver) > 0 { s.opts.Version = ver } if id := ctx.String("server_id"); len(id) > 0 { s.opts.Id = id } if addr := ctx.String("server_address"); len(addr) > 0 { s.opts.Address = addr } if adv := ctx.String("server_advertise"); len(adv) > 0 { s.opts.Advertise = adv } if s.opts.Action != nil { s.opts.Action(ctx) } return nil })) s.RLock() // pass in own name and version if s.opts.Service.Name() == "" { serviceOpts = append(serviceOpts, micro.Name(s.opts.Name)) } serviceOpts = append(serviceOpts, micro.Version(s.opts.Version)) s.RUnlock() fmt.Println(s.opts.Service) s.opts.Service.Init(serviceOpts...) s.Lock() srv := s.genSrv() srv.Endpoints = s.srv.Endpoints s.srv = srv s.Unlock() return nil }
作了如下事情
s.opts.Service.Init(serviceOpts...)
,就是調用micro.Init, 細節請見【micro server】最後看看service.Run()
func (s *service) Run() error { // generate an auth account srvID := s.opts.Service.Server().Options().Id srvName := s.Options().Name if err := authutil.Generate(srvID, srvName, s.opts.Service.Options().Auth); err != nil { return err } if err := s.start(); err != nil { return err } if err := s.register(); err != nil { return err } // start reg loop ex := make(chan bool) go s.run(ex) ch := make(chan os.Signal, 1) if s.opts.Signal { signal.Notify(ch, signalutil.Shutdown()...) } select { // wait on kill signal case sig := <-ch: if logger.V(logger.InfoLevel, logger.DefaultLogger) { logger.Infof("Received signal %s", sig) } // wait on context cancel case <-s.opts.Context.Done(): if logger.V(logger.InfoLevel, logger.DefaultLogger) { logger.Info("Received context shutdown") } } // exit reg loop close(ex) if err := s.deregister(); err != nil { return err } return s.stop() }
作了如下事情
調用s.start()
l.Close()
調用s.register()
定義退出信號chan,開協程go s.run(ex)
,監聽Shutdown信號、ctx.Done()信號,關閉chan
收到退出信號後,調用s.deregister()
, s.stop()
r.Deregister(s.srv)
,調用註冊中心的Deregister()方法s.stop()
流程與server相似,大同小異。
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