zoukankan      html  css  js  c++  java
  • 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
    ", 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/

  • 相关阅读:
    Python学习笔记之递归函数
    包管理工具-yum
    ElasticSearch定时清理缓存索引
    pytest实现多进程与多线程运行
    获取webView页面内窗体句柄
    文档测试
    ClickHouse 运维相关部分命令记录
    [转]contains a file system with errors, check forced
    log日志重复打印 修改
    jmeter参数化
  • 原文地址:https://www.cnblogs.com/xxswkl/p/14130121.html
Copyright © 2011-2022 走看看