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