「gin框架实现和设计分析」一次http请求的处理流程
写在前面
gin 框架是 golang web 开发中常用的一种框架,在工作中常常用到。这次就来一起看一看 gin 的设计和实现原理。本篇先从一个 http 请求的角度出发,看看 gin 框架对一次 http 请求的处理流程,在这个过程中逐一带出 gin 框架中的各个主要的组件。在后续的文章中,会对这些组件做相应的介绍(如果时间和精力允许的话)。在阅读本文之前,希望大家对 gin 框架的使用有一个基本的了解,文中会跳过一些基础内容。
那么就让我们开始吧~
请求处理流程图
上图就是 gin 框架对一次请求的完整处理流程。我们知道,gin 本质上是一个 web 应用服务,它通过实现
ServeHTTP(w http.ResponseWriter, req *http.Request)
接口来处理由 golang 的 http 服务层传递过来的请求。下面我们对图中涉及到的对象做简要介绍。
- http.Request:http.Request 对象由 golang 的 http 服务提供,它携带了一次 http 请求的 Method/Header/Body(http 请求的 Body 数据并不是直接读取并存储到 http.Request 对象中的,http.Request 对象只是封装了网络 I/O 对象,这里涉及到了 golang 的网络 I/O 实现,暂时不表)等信息。http.Request 的构成还是比较复杂的,留在后续章节探讨,大家有兴趣也可以根据下图自行研究。
- http.Responseriter:http.ResponseWriter 是一个 interface,它的实现类型是 http.response。http.response 用来接收 gin 应用服务对一次 http 请求的处理结果(包括Header和Body),它解耦了应用层和 http 服务层。比如,当用户调用 gin.Context 对象的 Header 方法和 Write 方法时,其实现就是向 http.response 中的缓冲区写入数据,之后由 http.response 来负责将数据返回给客户端。http.response 的构成同样也比较复杂,留在后续章节探讨,大家有兴趣也可以根据下图自行研究。
- engine:engine 对象是 gin 框架的处理引擎,就是它实现了
ServeHTTP(w http.ResponseWriter, req *http.Request)
interface。engine.ServeHTTP 方法中实现了一个 http 请求生命周期的所有处理步骤。的在官方文档的示例中,gin通过gin.Default()
初始化完成一个默认的 engine 对象。 - gin.Context:gin.Context 是 golang 上下文 Context interface 的实现类型。它贯穿了一次 http 请求从 路由匹配 --> 中间件处理 --> 业务逻辑处理 --> 返回结果 的整个处理周期。它封装了 http.Request 对象和 http.ResponseWriter 的实现类型。因此在 gin 框架中无论是中间件处理函数还是业务处理函数,它们的入参都只需要传入一个 gin.Context 指针即可。
- engine.pool:engine.pool 是 sync.Pool 类型的对象。因为 gin 应用需要为每一个 http 请求维护一个 gin.Context 对象,那么在高并发场景下就伴随着 gin.Context 对象的频繁生成和销毁。通过 sync.Pool 来对 gin.Context 对象进行复用,就能很大程度上减少生成和销毁 gin.Context 对象开销。这也是 gin 框架高性能的一个重要原因。
- engine.trees: engine.trees 是 gin 框架的路由树集合。之所以说它是一个集合,是因为 gin 会为每一种 httpMethod 单独生成一个路由树,这个路由树本质上是 radix tree 数据结构。gin 在通过 RouterGroup 注册路由和处理函数时,会传递 httpMethod,path和HandlerFunc 三个参数,最终通过 engine.addRoute 方法去执行注册。在 addRoute 方法中根据传入的 httpMethod 将处理函数注册到相应的路由树节点中。通过 RouterGroup 去注册生成 engine.trees 路由树的内容生成会放在后续章节中详细探讨,目前只要知道 engine.trees 在生成后的结构大致就是流程图中的样子。
engine.ServeHTTP 的实现
下面我们通过 engine.ServeHTTP 方法来看一下 gin 框架如何处理一次 http 请求。
engine.ServeHTTP 方法的实现非常简单,只做三件事:
-
向 engine.pool 申请一个 gin.Context 对象,并将 w 和 req 赋值给 gin.Context 对象。
-
在 engine.handleHTTPRequest 方法中,完成请求的路由匹配和请求的处理。在这里我们先不对路由树的匹配做详细分析,这部分内容留给后续章节。engine.handleHTTPRequest 的执行步骤请参考如下图中的代码注释
-
请求执行完毕,向 engine.pool 方法归还 gin.Context 对象。
总结
本篇先大致梳理了 gin 框架对一次 http 请求周期的处理流程。后续篇章中我们会深入分析 RouterGroup 对路由树注册和路由树匹配,gin.Context 的实现以及 http.Request 和 http.response 的实现等内容。
转载自:https://juejin.cn/post/7366526529521549324