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