gin源码

分析如下代码

func Index(c *gin.Context) {
    c.JSON(200, gin.H{"message":"kkkkkkkkk"})
    fmt.Println("first")
func main()
    {
        r := gin.Default()
        k := r.GET("/ping", Index)
        _ = k
        r.Run()
    }
View Code

1 gin.Default()如下,里面包含了New(),初始化engine的时候,它里面有属性trees,类型是type methodTrees []methodTree,这个属性之所以不放到RouterGroup里,是因为它是全局的只需有一个,并且RouterGroup里有engine,间接包含了它,

// 这个函数就是生成一个gin框架的基本对象,用于后续传入net/http进行处理,
func Default() *Engine {
    debugPrintWARNINGDefault()
    // 也可以直接用New(),相当于省去了两个中间件,一个是日志输出,一个是处理异常,
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}

func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            Handlers: nil,
            basePath: "/",
            root:     true,
        },
        FuncMap:                template.FuncMap{},
        RedirectTrailingSlash:  true,
        RedirectFixedPath:      false,
        HandleMethodNotAllowed: false,
        ForwardedByClientIP:    true,
        AppEngine:              defaultAppEngine,
        UseRawPath:             false,
        RemoveExtraSlash:       false,
        UnescapePathValues:     true,
        MaxMultipartMemory:     defaultMultipartMemory,
        // trees就是个存放map的数组,key是http请求的方法名,类型为string,
        // values是对应生成的前缀树,每个方法生成一个,9是所有不同请求种类的个数,
        // 这些方法名在net/http中就定义了,
        trees:                  make(methodTrees, 0, 9),
        delims:                 render.Delims{Left: "{{", Right: "}}"},
        secureJsonPrefix:       "while(1);",
    }
    // 这里本质上是循环引用,这样方便后续的调用,
    engine.RouterGroup.engine = engine
    engine.pool.New = func() interface{} {
        return engine.allocateContext()
    }
    return engine
}
// 这个是为路由添加中间件的函数,传入的是个可变参数,注意本质上是添加到engine下的RouterGroup中了,
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...)
    engine.rebuild404Handlers()
    engine.rebuild405Handlers()
    return engine
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}
View Code

2 Get请求

// 初始化时得到的engine对象实现了IRoutes接口,里面定义了所有路由处理方法,该接口里的http请求方法engine并没有直接实现,而是
// 继承了它里面RouterGroup的实现,
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle(http.MethodGet, relativePath, handlers)
// 然后添加上请求的方法名继续调用RouterGroup的handle方法,
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    // 得到绝对路径
    absolutePath := group.calculateAbsolutePath(relativePath)
    // 得到整个处理链的所有函数,实际上就是把之前的中间件的函数加进来,因为执行这个路由函数之前要先执行完中间件的才行,
    handlers = group.combineHandlers(handlers)
    // 这个是最重要的一步,这个就是注册路由,在RouterGroup中设置engine属性,并且为指针类型,为了就是让它成为全局的,方便赋值调用,
    group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}
// 注册路由最核心的部分,
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    // 先判断一下传入的URL格式是否正确,
    assert1(path[0] == '/', "path must begin with '/'")
    assert1(method != "", "HTTP method can not be empty")
    assert1(len(handlers) > 0, "there must be at least one handler")

    debugPrintRoute(method, path, handlers)
    // 如果已经有该方法的树了就不新建了,直接在上面查找修改,否则新建一个,
    root := engine.trees.get(method)
    if root == nil {
        root = new(node)
        root.fullPath = "/"
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    // 这个函数本质上是借用的httprouter的原理,就是生成对应的前缀树,叶节点为gin在该路由下所有要处理的函数,
    root.addRoute(path, handlers)
}
View Code

3 Run()函数

// 运行gin,监听端口,接受到请求后运行,
func (engine *Engine) Run(addr ...string) (err error) {
    defer func() { debugPrintError(err) }()
    // 这里是规定监听的端口,
    address := resolveAddress(addr)
    debugPrint("Listening and serving HTTP on %s\n", address)
    // 注意这里直接调用的http的方法,它的第二个接受参数类型是Handler接口,该接口只有一个方法ServerHttp
    err = http.ListenAndServe(address, engine)
    return
}
// 这个方法极其重要,该方法的作用就是把请求的对象封装成Request,把要返回的对象放入ResponseWriter中,
// enginge实现了该接口,所以也实现了该方法,
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
// 之后启动服务,发起请求后,engine就会调用这个方法,
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()
    // 这个就是传入上下文对象,找到对应的处理链函数,并处理
    engine.handleHTTPRequest(c)

    engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(c *Context) {
    httpMethod := c.Request.Method
    rPath := c.Request.URL.Path
    unescape := false
    if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
        rPath = c.Request.URL.RawPath
        unescape = engine.UnescapePathValues
    }

    if engine.RemoveExtraSlash {
        rPath = cleanPath(rPath)
    }

    // Find root of the tree for the given HTTP method
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // Find route in tree
        // getValues是这个函数里的关键,用于由之前建立的前缀树取出对应的处理链,
        value := root.getValue(rPath, c.Params, unescape)
        if value.handlers != nil {
            c.handlers = value.handlers
            c.Params = value.params
            c.fullPath = value.fullPath
            // 一旦调用Next函数,就得到了整个链条的控制权,会顺序将整个链条上的函数执行完,
            c.Next()
            c.writermem.WriteHeaderNow()
            return
        }
        if httpMethod != "CONNECT" && rPath != "/" {
            if value.tsr && engine.RedirectTrailingSlash {
                redirectTrailingSlash(c)
                return
            }
            if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
                return
            }
        }
        break
    }

    if engine.HandleMethodNotAllowed {
        for _, tree := range engine.trees {
            if tree.method == httpMethod {
                continue
            }
            if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
                c.handlers = engine.allNoMethod
                serveError(c, http.StatusMethodNotAllowed, default405Body)
                return
            }
        }
    }
    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
}
// 特别注意这个Next函数是全局的,这样保证了链条上的函数只被执行一次,不会重复执行,
func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++
    }
}
View Code

4 路由分组源码,虽然路由分组了,但是用的仍是同一个树,只是各自新建了一个RouterGroup对象,下面存放各自的处理链函数,也即中间件,

func loginEndpoint(c *gin.Context)  { fmt.Println("loginEndpoint")}
func submitEndpoint(c *gin.Context)  { fmt.Println("submitEndpoint")}
func readEndpoint1(c *gin.Context)  { fmt.Println("readEndpoint1")}
func readEndpoint2(c *gin.Context)  { fmt.Println("readEndpoint2")}
func midv1(c *gin.Context)  { fmt.Println("这是v1的中间件")}
func midv2(c *gin.Context)  { fmt.Println("这是v2的中间件")}
func main() {
    gin_engine := gin.Default()
    // 简单的路由组: v1
    // v1是RouterGroup类型,
    v1 := gin_engine.Group("/v1")
    // RouterGroup和engine都实现了Use方法,engine只是重写了该方法,里面用engine下的RouterGroup调用了该方法,
    v1.Use(midv1)
    v1.POST("/login", loginEndpoint)
    v1.POST("/submit", submitEndpoint)
    v1.POST("/read", readEndpoint1)
    // 简单的路由组: v2
    v2 := gin_engine.Group("/v2")
    {   v2.Use(midv2)
        v2.POST("/login", loginEndpoint)
        v2.POST("/submit", submitEndpoint)
        v2.POST("/read", readEndpoint2)
    }
    gin_engine.Run(":8080")
}
// Use就是把中间件加到了处理链中,
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}
// 通过这个可以看出v1里面有自己的HandlerChain,这里面就是专门为了存放gin_engine的中间件加上它私有的中间件,
type RouterGroup struct {
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}
// 新建路由组的时候,把之前gin_engine下的RouterGroup的中间件也加进来了,
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
    return &RouterGroup{
        Handlers: group.combineHandlers(handlers),
        basePath: group.calculateAbsolutePath(relativePath),
        engine:   group.engine,
    }
}
View Code

参考:https://juejin.cn/post/6844904168193277965 

https://www.shipengqi.top/2019/11/21/gin-resource-code-analysis/

posted on 2020-12-13 20:30  吃我一枪  阅读(255)  评论(0编辑  收藏  举报

导航