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
}
做了以下事件
- 初始化并设置 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 相似,大同小异。