概述
go-zero 基于 net/http 标准库实现了一套 rest web 框架。在使用 goctl 快速开发的同时,也需要了解 go-zero 内部做了什么。本文结合 go-zero rest学习其中的源码,力图做到知其所以然。
源码
流程图

在阅读源码之前,先看下流程图有个印象。从流程图大致可以看出来:
- go-zero 会创建路由组,其中按顺序注册了几类 handler(中间件),最后在 business handler 处理业务逻辑。
大致有个印象后开始源码走读。
源码走读
启动 api 服务:
1func main() {
2 ...
3 server := rest.MustNewServer(c.RestConf)
4 defer server.Stop()
5
6 ctx := svc.NewServiceContext(c)
7 handler.RegisterHandlers(server, ctx)
8
9 fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
10 server.Start()
11}
启动服务主要做了三件事:
- 创建服务端 server
- 注册 handler 到 server
- 启动服务端 server
按顺序介绍。
创建服务端 server
1func MustNewServer(c RestConf, opts ...RunOption) *Server {
2 // NewServer 创建 server
3 server, err := NewServer(c, opts...)
4 if err != nil {
5 logx.Must(err)
6 }
7
8 return server
9}
10
11func NewServer(c RestConf, opts ...RunOption) (*Server, error) {
12 // c.SetUp 启动 Prometheus,tracing, profiling 等服务
13 if err := c.SetUp(); err != nil {
14 return nil, err
15 }
16
17 server := &Server{
18 ngin: newEngine(c),
19 router: router.NewRouter(),
20 }
21
22 ...
23 return server, nil
24}
创建 server 实际创建的是 server 的 engine 和 router。
engine 主要结构如下:
1type engine struct {
2 // server 的配置
3 conf RestConf
4 routes []featuredRoutes // 业务路由
5 // 调用链
6 chain chain.Chain
7 // 中间件
8 middlewares []Middleware
9 ...
10}
11
12func newEngine(c RestConf) *engine {
13 svr := &engine{
14 conf: c,
15 timeout: time.Duration(c.Timeout) * time.Millisecond,
16 }
17 ...
18}
router 结构如下:
1func NewRouter() httpx.Router {
2 return &patRouter{
3 trees: make(map[string]*search.Tree),
4 }
5}
6
7type Router interface {
8 http.Handler
9 Handle(method, path string, handler http.Handler) error
10 SetNotFoundHandler(handler http.Handler)
11 SetNotAllowedHandler(handler http.Handler)
12}
patRouter 包含路由信息,其实现了 Router 接口。
创建了 server 后还需要注册路由 handler 到 server,这样服务端才能根据路由找到对应的 handler 处理。
注册路由 handler
1func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
2 // server.AddRoutes 注册路由 handler
3 server.AddRoutes(
4 []rest.Route{
5 {
6 Method: http.MethodGet,
7 Path: "/ping",
8 Handler: pingHandler(serverCtx),
9 },
10 },
11 )
12}
13
14func (s *Server) AddRoutes(rs []Route, opts ...RouteOption) {
15 // 自定义的业务路由将被封装到 featuredRoutes 对象
16 r := featuredRoutes{
17 routes: rs,
18 }
19 for _, opt := range opts {
20 opt(&r)
21 }
22
23 // 将 featuredRoutes 添加到 Server.engine
24 s.ngin.addRoutes(r)
25}
26
27func (ng *engine) addRoutes(r featuredRoutes) {
28 ...
29 // 实际是将路由组添加到 engine.routes 中
30 ng.routes = append(ng.routes, r)
31}
业务路由注册完,接下来将进入启动 server,这是需要关注的重点。
启动 server
1func (s *Server) Start() {
2 // 调用 Server.engine.start 启动服务端 server
3 handleError(s.ngin.start(s.router))
4}
5
6func (ng *engine) start(router httpx.Router, opts ...StartOption) error { // engine.bindRoutes 绑定路由到 router
7 if err := ng.bindRoutes(router); err != nil {
8 return err
9 }
10
11 ...
12 return internal.StartHttps(ng.conf.Host, ng.conf.Port, ng.conf.CertFile,
13 ng.conf.KeyFile, router, opts...)
14}
15
16func (ng *engine) bindRoutes(router httpx.Router) error {
17 // engine.routes
18 for _, fr := range ng.routes {
19 // 绑定 rest.featuredRoutes
20 if err := ng.bindFeaturedRoutes(router, fr, metrics); err != nil {
21 return err
22 }
23 }
24
25 return nil
26}
27
28func (ng *engine) bindFeaturedRoutes(router httpx.Router, fr featuredRoutes, metrics *stat.Metrics) error {
29 ...
30 for _, route := range fr.routes {
31 if err := ng.bindRoute(fr, router, metrics, route, verifier); err != nil {
32 return err
33 }
34 }
35
36 return nil
37}
38
39func (ng *engine) bindRoute(fr featuredRoutes, router httpx.Router, metrics *stat.Metrics,
40 route Route, verifier func(chain.Chain) chain.Chain) error {
41 // engine.chain,初始化为 nil
42 chn := ng.chain
43 if chn == nil {
44 // engine.buildChainWithNativeMiddlewares 注册自带中间件到 engine.chain
45 chn = ng.buildChainWithNativeMiddlewares(fr, route, metrics)
46 }
47
48 // 添加 AuthHandler 到 engine.chain 中
49 chn = ng.appendAuthHandler(fr, chn, verifier)
50
51 // 将自定义中间件注册到 engine.chain
52 for _, middleware := range ng.middlewares {
53 chn = chn.Append(convertMiddleware(middleware))
54 }
55
56 // engine.chain.ThenFunc 将 handler 串联成 handler
57 handle := chn.ThenFunc(route.Handler)
58
59 return router.Handle(route.Method, route.Path, handle)
60}
启动 server 的重点在 engine.bindRoute。
其中,engine.buildChainWithNativeMiddlewares 注册 go-zero 自带中间件:
1func (ng *engine) buildChainWithNativeMiddlewares(fr featuredRoutes, route Route,
2 metrics *stat.Metrics) chain.Chain {
3 chn := chain.New()
4 ...
5 // MaxConns 用于并发控制
6 if ng.conf.Middlewares.MaxConns {
7 chn = chn.Append(handler.MaxConnsHandler(ng.conf.MaxConns))
8 }
9 if ng.conf.Middlewares.Breaker {
10 chn = chn.Append(handler.BreakerHandler(route.Method, route.Path, metrics))
11 }
12 ...
13}
类似的,自定义中间件通过 chn.Append(convertMiddleware(middleware)) 注册到 engine.chain 中。
接着调用 chain.ThenFunc 串联中间件成 handler:
1func (c chain) ThenFunc(fn http.HandlerFunc) http.Handler {
2 ...
3 return c.Then(fn)
4}
5
6func (c chain) Then(h http.Handler) http.Handler {
7 if h == nil {
8 h = http.DefaultServeMux
9 }
10
11 // 这段代码很有意思,它将所有中间件按顺序串联起来组成一个 handler
12 // 调用这个 handler 处理时会经过后续一系列的中间件,最终到业务 handler 处理
13 // 具体可参考 https://github.com/zeromicro/go-zero/blob/master/rest/chain/chain.go#L81
14 for i := range c.middlewares {
15 h = c.middlewares[len(c.middlewares)-1-i](h)
16 }
17
18 return h
19}
最后将该 handler 和路由信息注册到 router 中,后续服务端根据请求在 router 中查找对应的 handler 处理。
1func (pr *patRouter) Handle(method, reqPath string, handler http.Handler) error {
2 ...
3
4 tree, ok := pr.trees[method]
5 if ok {
6 return tree.Add(cleanPath, handler)
7 }
8
9 tree = search.NewTree()
10 pr.trees[method] = tree
11 return tree.Add(cleanPath, handler)
12}
详细流程如下图:

小结
本文介绍了 go-zero rest 的源码是怎么处理请求的。从源码也可以看出每个请求背后是一系列中间件 handler 在处理,并且 server 启动了 Prometheus,Trace 等服务负责监控,链路追踪等,使得开发微服务时只需要关注业务逻辑即可,非常方便。