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}

做了以下事件

  1. 初始化并设置options,其中Options.Service是micro
  2. 初始化web.service{},其中mux初始化了http.ServeMux
  3. s.genSrv()生成registry.Service{}

    1. 获取ip:port并验证
    2. 返回registry.Service{}
  4. 返回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}

做了以下事件

  1. 设置options
  2. 整顿serviceOpts
  3. 调用s.opts.Service.Init(serviceOpts...),就是调用micro.Init, 细节请见【micro server】
  4. 获取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()}

做了以下事件

  1. 拿到srvID,srvName, 生成auth account
  2. 调用s.start()

    1. 顺次执行s.opts.BeforeStart()
    2. s.listen("tcp", s.opts.Address)监听端口
    3. 有自定义的s.opts.Handler就用自定义的,没有就设置下html目录
    4. go httpSrv.Serve(l),开始web服务
    5. 顺次执行s.opts.AfterStart()
    6. 开协程监听退出信号, 收到信号调用l.Close()
  3. 调用s.register()

    1. 从micro拿到Registry
    2. 从新取registry.Service{}, 每次都从新取啊,正文说是在流程中node地址可能扭转
    3. s.opts.RegisterCheck(),注册逻辑和server一样,详情见【micro server】
  4. 定义退出信号chan,开协程go s.run(ex),监听Shutdown信号、ctx.Done()信号,敞开chan

    1. 定时向注册核心注册,收到退出信号后进行,并调用t.Stop()
  5. 收到退出信号后,调用s.deregister()s.stop()

    1. s.deregister() -> r.Deregister(s.srv),调用注册核心的Deregister()办法
    2. s.stop()

      1. 顺次调用s.opts.BeforeStop()
      2. 告诉退出信号,s.running = false
      3. 顺次调用s.opts.AfterStop()

流程与server相似,大同小异。