61e20641d0bde6b9feddde25a4202b9c
GIN 源码阅读

先看简单的demo:

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() // listen and serve on 0.0.0.0:8080
}
  • 先看 gin.Default:
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
        debugPrintWARNINGDefault()
        engine := New()
        engine.Use(Logger(), Recovery())
        return engine
}
  • engine := New() 所返回的结构体:
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,
                UnescapePathValues:     true,
                MaxMultipartMemory:     defaultMultipartMemory,
                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.Use:
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
        engine.RouterGroup.Use(middleware...)
        engine.rebuild404Handlers()
        engine.rebuild405Handlers()
        return engine
}

engine.RouterGroup.Use:

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
        group.Handlers = append(group.Handlers, middleware...)
        return group.returnObj()
    }
  • 接下来回到demo,看 r.Run():
func (engine *Engine) Run(addr ...string) (err error) {
        defer func() { debugPrintError(err) }()

        address := resolveAddress(addr)
        debugPrint("Listening and serving HTTP on %s\n", address)
        err = http.ListenAndServe(address, engine)
        return
}

看过 net/http 的同学应该知道,在Go里只要实现 ServeHTTP 就可以,所以我们找一下:

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)
}

这就是处理流程,请求来了,从 engine.pool 里拿一个空的context,丢到 engine.handleHTTPRequest 处理,然后回收。

  • engine.handleHTTPRequest:

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

    // 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 {
                    root := t[i].root
                    // Find route in tree
                    handlers, params, tsr := root.getValue(path, c.Params, unescape)
                    if handlers != nil {
                            c.handlers = handlers
                            c.Params = params
                            c.Next()
                            c.writermem.WriteHeaderNow()
                            return
                    }
                    if httpMethod != "CONNECT" && path != "/" {
                            if 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 {
                            if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
                                    c.handlers = engine.allNoMethod
                                    serveError(c, 405, default405Body)
                                    return
                            }
                    }
            }
    }
    c.handlers = engine.allNoRoute
top Created with Sketch.