内容纲要
前情提要
前文我们主要看了 Engine 的请求处理流程,其中重点研究了 engine.ServerHTTP 和 engine. handleHTTPRequest 两个函数方法的处理流程。其中出现了几个比较重要的问题还没研究:
- trees
- node
现在,我们来揭开它们的面纱。
trees
跟踪源码得知:
- trees 是 Engine 的一个变量
- trees 的类型是
methodTrees
type methodTrees []methodTree
type methodTree struct { method string root *node }
可以看出,trees 本质上是一个包含两个变量(method、root)的结构体的数组。
node
node 结构体
从 trees 的类型里面,我们可以看到有个 root 变量,它的类型是 node,这就是 trees 与 node 之间的关系。下面我们来看看 node 到底是什么。
// node 源码
type node struct {
path string
indices string
children []*node
handlers HandlersChain
priority uint32
nType nodeType
maxParams uint8
wildChild bool
fullPath string
}
这个类型信息量还是挺大的,从其中几个变量能看出不少问题:
- children []*node
- 这是一个包含自身类型(node)的指针列表
- 可见这可以是个树状结构
- 这是一个包含自身类型(node)的指针列表
- handlers HandlersChain
- 之前的研究中也出现过无数次的 handler 这个词
- 可以猜到这个变量即用来指向每个请求的处理函数的
- 之前的研究中也出现过无数次的 handler 这个词
- path string
- 请求路径,显而易见
- nType nodeType
- node 类型(从源码中可以看到有:static、root、param、catchAll 这么几个枚举常量)
- 其它的先不说,但可以联想到前面的 trees 里的 root 应该就是对应的这个
- node 类型(从源码中可以看到有:static、root、param、catchAll 这么几个枚举常量)
node 操作函数(方法)
node 结构体总共有一下 5 个操作函数。从它们的注释,我们对 node 的功能也能有一个比较清晰的认识了。
// 增加给定子级的优先级,必要时重新排序
func (n *node) incrementChildPrio(pos int) int {...}
// (原注释并不是这么解释的,这是我初步看过源码后总结的)
// 将请求处理方法绑定的节点(node)添加到节点树里
// 如果节点树是空的,则作为 root 节点
// addRouter 的调用方是 Engine 的爸爸 RouteGroup(下章我们就详细看看这个是什么)
func (n *node) addRoute(path string, handlers HandlersChain) {...}
// (作为内部函数被 addRoute 调用,主要功能就是指向插入节点的操作)
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {...}
// 返回给点路径的节点信息(句柄等)
// (前文中的 trees 那个 for 循环中有对 root 节点进行 getValue 的操作,即是这个函数)
func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) {...}
// 对路径进行不区分大小写的查找,并可以修复末尾斜杆问题,返回校正的路径
// (跟 Engine 的对应参数进行配合使用)
func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) {...}
小结
经过 3 篇文章的分析,我们大概已经理清了一个 Gin 服务从创建到请求处理的整个基本流程。
- 实例化一个 Engine 结构体
- 通过 net/http 库把 Engine 实例和 http.Server 进行绑定
- net/http 启动监听服务
- 接到请求将请求转发到 Engine 的 ServeHTTP 接口,并调用 handleHTTPRequest 进行请求处理
- handleHTTPRequest 对 path 进行处理,并通过查询 trees 的节点,得到请求 handlers
- handlers 被交给 Context 进行真正的 handler 调用并返回请求结果