关于micro:go-micro-web

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.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
}

做了以下事件

  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相似,大同小异。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理