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

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

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

浙公网安备 33010602011771号