概述
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 等服务负责监控,链路追踪等,使得开发微服务时只需要关注业务逻辑即可,非常方便。
参考资料
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |