淌统阎剂概述go-zero 基于 net/http 标准库实现了一套 rest web 框架。在使用 goctl 快速开发的同时也需要了解 go-zero 内部做了什么。本文结合 go-zero rest学习其中的源码力图做到知其所以然。源码流程图image在阅读源码之前先看下流程图有个印象。从流程图大致可以看出来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按顺序介绍。创建服务端 serverfunc MustNewServer(c RestConf, opts ...RunOption) *Server {// NewServer 创建 serverserver, err : NewServer(c, opts...)if err ! nil {logx.Must(err)}return server}func NewServer(c RestConf, opts ...RunOption) (*Server, error) {// c.SetUp 启动 Prometheustracing, 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 RestConfroutes []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.HandlerHandle(method, path string, handler http.Handler) errorSetNotFoundHandler(handler http.Handler)SetNotAllowedHandler(handler http.Handler)}patRouter 包含路由信息其实现了 Router 接口。创建了 server 后还需要注册路由 handler 到 server这样服务端才能根据路由找到对应的 handler 处理。注册路由 handlerfunc RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {// server.AddRoutes 注册路由 handlerserver.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.engines.ngin.addRoutes(r)}func (ng *engine) addRoutes(r featuredRoutes) {...// 实际是将路由组添加到 engine.routes 中ng.routes append(ng.routes, r)}业务路由注册完接下来将进入启动 server这是需要关注的重点。启动 serverfunc (s *Server) Start() {// 调用 Server.engine.start 启动服务端 serverhandleError(s.ngin.start(s.router))}func (ng *engine) start(router httpx.Router, opts ...StartOption) error { // engine.bindRoutes 绑定路由到 routerif 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.routesfor _, fr : range ng.routes {// 绑定 rest.featuredRoutesif 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初始化为 nilchn : ng.chainif chn nil {// engine.buildChainWithNativeMiddlewares 注册自带中间件到 engine.chainchn ng.buildChainWithNativeMiddlewares(fr, route, metrics)}// 添加 AuthHandler 到 engine.chain 中chn ng.appendAuthHandler(fr, chn, verifier)// 将自定义中间件注册到 engine.chainfor _, middleware : range ng.middlewares {chn chn.Append(convertMiddleware(middleware))}// engine.chain.ThenFunc 将 handler 串联成 handlerhandle : 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 串联中间件成 handlerfunc (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#L81for 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] treereturn tree.Add(cleanPath, handler)}详细流程如下图image小结本文介绍了 go-zero rest 的源码是怎么处理请求的。从源码也可以看出每个请求背后是一系列中间件 handler 在处理并且 server 启动了 PrometheusTrace 等服务负责监控链路追踪等使得开发微服务时只需要关注业务逻辑即可非常方便。