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() }
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() }
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) }
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++ } }
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, } }
参考:https://juejin.cn/post/6844904168193277965
https://www.shipengqi.top/2019/11/21/gin-resource-code-analysis/