examples/web 有一个web的例子,这里比较简单
service.HandleFunc("/", helloWorldHandler)
这一行指定处理程序比较简单,第2个参数定义了一个函数,只有满足条件就行
handler func(http.ResponseWriter, *http.Request)
理论我的项目中不太可能只用go micro, 从0开始手撸所有其余轮子,那么可不可以在go micro中引入罕用的框架呢?
当然能够,来看一个引入gin的例子examples/greeter/api/gin/gin.go
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
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等例子
上面看看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.Servicefunc 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}
做了以下事件
- 初始化并设置options,其中Options.Service是micro
- 初始化web.service{},其中mux初始化了http.ServeMux
s.genSrv()生成registry.Service{}
- 获取ip:port并验证
- 返回registry.Service{}
- 返回web.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}
做了以下事件
- 设置options
- 整顿serviceOpts
- 调用
s.opts.Service.Init(serviceOpts...)
,就是调用micro.Init, 细节请见【micro server】 - 获取registry.Service{}, 复制给web.srv, 这一步在newService()中曾经做了,这里可能是版本还在迭代中,反复了
最初看看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()}
做了以下事件
- 拿到srvID,srvName, 生成auth account
调用
s.start()
- 顺次执行s.opts.BeforeStart()
- s.listen("tcp", s.opts.Address)监听端口
- 有自定义的s.opts.Handler就用自定义的,没有就设置下html目录
- go httpSrv.Serve(l),开始web服务
- 顺次执行s.opts.AfterStart()
- 开协程监听退出信号, 收到信号调用
l.Close()
调用
s.register()
- 从micro拿到Registry
- 从新取registry.Service{}, 每次都从新取啊,正文说是在流程中node地址可能扭转
- s.opts.RegisterCheck(),注册逻辑和server一样,详情见【micro server】
定义退出信号chan,开协程
go s.run(ex)
,监听Shutdown信号、ctx.Done()信号,敞开chan- 定时向注册核心注册,收到退出信号后进行,并调用t.Stop()
收到退出信号后,调用
s.deregister()
,s.stop()
- s.deregister() ->
r.Deregister(s.srv)
,调用注册核心的Deregister()办法 s.stop()
- 顺次调用s.opts.BeforeStop()
- 告诉退出信号,s.running = false
- 顺次调用s.opts.AfterStop()
- s.deregister() ->
流程与server相似,大同小异。