1 最小程序
首先通过对一个最小程序的分析,说明一下Gin程序的基本运行流程以及核心的组件。下面这个程序启动过后返回一个JSON字符串,{“message” : “pong”}。
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run()
}
下面我将深入代码,将整个程序的运行逻辑呈现在你的面前,由于本节仅仅是一个基础的介绍,因此主要部分结构和方法会进行删减,进保留主干。
2 创建引擎
r := gin.Default()
第1行语句创建一个gin运行引擎
func Default() *Engine {
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
该函数创建一个默认的Engine对象,Engine对象故名思义是 gin 的运行引擎,关键的字段如下所示:
type Engine struct {
RouterGroup
pool sync.Pool
trees methodTrees
}
func New() *Engine {
engine := &Engine{
// 初始化RouterGroup
RouterGroup: RouterGroup{ ... },
// 创建方法输,由于有9种方法
trees: make(methodTrees, 0, 9),
}
// 创建Context对象池
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
- RouterGroup:管理中间件,engine.User(Logger(), Recovery()) 就是将日志和恢复中间件添加到 RouterGroup中进行管理。
- pool:在进行HTTP请求处理的过程中需要采用上下文Context进行管理,由于需要频繁的创建和销毁Context因此采用sync.Pool提高效率。
- tree:每一个 HTTP 方法会有一颗方法树,方法树记录了路径和路径上的处理函数。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
// 将中间件添加到RouterGroup中
engine.RouterGroup.Use(middleware...)
return engine
}
// 中间件,实际上是一个参数为Context的函数
type HandlerFunc func(*Context)
// 管理中间件的是一个切片
type HandlersChain []HandlerFunc
type RouterGroup struct {
Handlers HandlersChain
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
上面的方面说明了注册中间件的过程,RouterGroup的核心字段是一个HandlerFunc的切片,所有的中间件按照顺序保存在这个切片中。
3 注册处理方法
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
第2行语句,注册一个URL为/ping的处理方法,该方法返回一个JSON。
// GET方法
const (
MethodGet = "GET"
)
// 调用handle完成注册
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
// 获取完成的路径
absolutePath := group.calculateAbsolutePath(relativePath)
// 将中间件方法和本URL处理方法组合形成一个方法链,然后注册到tree中
handlers = group.combineHandlers(handlers)
// 整个方法注册的核心函数,完成方法的注册
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
engine.addRoute是方法注册的入口函数,简单来说就是将这个URL的方法链注册到相应的方法树中,为了提高URL的检索效率,这个方法树采用了Radix Tree的数据结构,在本小节中可以忽略,总值一句话通过这个方法树可以高效的检索的每个URL的对应处理方法链。
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
// 获得这个方法的方法(GET方法)数据
root := engine.trees.get(method)
// 将该URL的处理方法链添加到方法树中
root.addRoute(path, handlers)
}
// addRoute adds a node with the given handle to the path.
// Not concurrency-safe!
func (n *node) addRoute(path string, handlers HandlersChain) {
fullPath := path
...
// 创建相应的叶子结点或者找到相应的叶子节点
n = child
...
n.handlers = handlers
n.fullPath = fullPath
return
}
type node struct {
handlers HandlersChain
fullPath string
}
可以看到每个每个方法树中的节点包括 handlers和fullPath两个字段,分别保存相应的URL路径和处理方法链。
4 系统运行
r.Run()
第3行语句,调用Go的HTTP包,启动WEB服务。
func (engine *Engine) Run(addr ...string) (err error) {
// 获取地址
address := resolveAddress(addr)
// http监听并且服务
err = http.ListenAndServe(address, engine)
return
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Engine实现了ServeHTTP方法,该方法是HTTP的回调接口,当HTTP模块接收到一个HTTP请求后将调用Engine的ServeHTTP方法。
5 处理HTTP请求
简单来说就是从方法树中找到相应的方法进行处理,但是由于存在中间件,因此需要在中间件中通过Context保存上下文的数据,具体个过程在中间件一节在详细说。
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 从对象池中获取一个Context
c := engine.pool.Get().(*Context)
// 对Context进行必要的初始化
c.writermem.reset(w)
c.Request = req
c.reset()
// 处理该HTTP请求
engine.handleHTTPRequest(c)
// 完成对HTTP的请求后,释放该Context
engine.pool.Put(c)
}
func (engine *Engine) handleHTTPRequest(c *Context) {
// 获得HTTP请求方法类型
httpMethod := c.Request.Method
// 获得URL路径
rPath := c.Request.URL.Path
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
// 获得该HTTP请求的方法树
if t[i].method != httpMethod {
continue
}
root := t[i].root
// 找到该URL的处理方法链
value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.handlers != nil {
// 通过上下文的Next方法实现中间件的调用以及方法的调用
c.handlers = value.handlers
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
}
}
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付