当前位置: 首页 > news >正文

go-zero rest 源码学习笔记


概述

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

按顺序介绍。

创建服务端 server

func 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 启动 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 注册路由 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,这是需要关注的重点。

启动 server

func (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.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,初始化为 nilchn := ng.chain  if 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 串联中间件成 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#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] = tree  return tree.Add(cleanPath, handler)  
}

详细流程如下图:

image

小结

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

参考资料

  • go-zero rest
  • Go 责任链模式

http://www.gsyq.cn/news/159753.html

相关文章:

  • MBA必看!9个高效降aigc工具推荐,轻松应对AI检测
  • Kafka Streams实战:轻量级大数据流处理框架
  • 基于GA-BP的电涡流传感器称重系统温度补偿附matlab代码
  • Java计算机毕设之基于SpringBoot+微服务教材征订系统基于SpringBoot的高校教材征订管理系统(完整前后端代码+说明文档+LW,调试定制等)
  • 【建议收藏】别再只会写 CRUD 了!2025年程序员必须要掌握的“全栈AI”架构演进方案
  • 毕业季 “查重刺客” 退退退:paperzz 降重 / 降 AIGC,一键把论文从 “高危区” 拉回 “安全线”
  • spring-事务
  • 毕业季 “论文加速器”:paperzz 毕业论文功能,让学术创作少走弯路
  • 论文「双降难题」破局:从重复率到 AIGC 疑似度,paperzz 降重 / 降 AIGC 功能一次搞定
  • 计算机Java毕设实战-基于微服务教材征订系统基于SpringBoot+vue的教材管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 小童童装怎么选?这篇高性价比推荐攻略宝妈必收藏 - 品牌测评鉴赏家
  • Week 9
  • 为了过知网检测,我自费测了十大降AI平台,最好用的都在这了
  • 纯干货无广:基于真实体验的十大好用降AI工具红黑榜
  • scrapy基础知识之发送GET请求
  • 国货童装童鞋大揭秘!这12个品牌让宝妈闭眼入 - 品牌测评鉴赏家
  • 【Python基础】Python字符串操作全攻略:新手入门必备指南
  • Node.js 回调函数
  • Django Cookie/Session:深入理解与实践
  • 2026微信公众号服务号、订阅号、小程序、企业微信、微信开放平台、微信开发者平台区别
  • CSS定位的特殊应用
  • Mozz TCAD丨晶体-器件坐标系的精准映射:MOZZ 各向异性模型的坐标配准方法
  • AI Agent在心理健康领域的应用:情绪支持与干预
  • PostgreSQL 时间/日期处理指南
  • 为什么fastlio的frame_id是carmera_init
  • Doris资源组管理:精细化资源分配策略
  • AI搜索重构流量格局,品牌如何制定下一代可见度战略?
  • 隧道区域定位:黑暗中的“智慧灯塔”,筑牢地下空间安全防线
  • 只要十分钟,AI率从89%降到13%!2025年度十大降AI工具推荐
  • 洗涤类消泡剂的作用场景在什么地方