gin的路由算法流程

gin的路由算法

gin的是路由算法其实就是一个Trie树(也就是前缀树).     有关数据结构的可以自己去网上找相关资料查看.

1. 注册路由预处理

我们在使用gin时通过下面的代码注册路由

2. 普通注册

router.POST("/somePost", func(context *gin.Context) {
    context.String(http.StatusOK, "some post")
    })

3. 使用中间件注册

router.Use(Logger())

4.使用Group

v1 := router.Group("v1"){
    v1.POST("login", func(context *gin.Context) {
            context.String(http.StatusOK, "v1 login")
            }
    )
}

5. 具体实现

// routergroup.go:L72-77
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    	absolutePath := group.calculateAbsolutePath(relativePath) // <---
 	handlers = group.combineHandlers(handlers) // <---
 	group.engine.addRoute(httpMethod, absolutePath, handlers)
 	return group.returnObj()
  }

在调用POST, GET, HEAD等路由HTTP相关函数时, 会调用handle函数
如果调用了中间件的话, 会调用下面函数

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    	group.Handlers = append(group.Handlers, middleware...)
        return group.returnObj()
  }

如果使用了Group的话, 会调用下面函数

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
    return &RouterGroup{
            Handlers: group.combineHandlers(handlers),
            basePath: group.calculateAbsolutePath(relativePath),        
            engine:   group.engine,    
            }
}

重点关注下面两个函数:

// routergroup.go:L208-217
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {    
	finalSize := len(group.Handlers) + len(handlers)    
	if finalSize >= int(abortIndex) {        
		panic("too many handlers")	
	}    
	
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)    
	copy(mergedHandlers[len(group.Handlers):], handlers)    
	return mergedHandles
}
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
    return joinPaths(group.basePath, relativePath)
}

func joinPaths(absolutePath, relativePath string) string {

    if relativePath == "" {
            return absolutePath
    }
    
    finalPath := path.Join(absolutePath, relativePath)    
    appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/'
    if appendSlash {
            return finalPath + "/"   
    } 
    
    return finalPath
 }

在joinPaths函数里面有段代码, 很有意思, 我还以为是写错了. 主要是path.Join的用法.

finalPath := path.Join(absolutePath, relativePath)
appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/'

在当路由是/user/这种情况就满足了lastChar(relativePath) == ‘/’ && lastChar(finalPath) != ‘/’. 主要原因是path.Join(absolutePath, relativePath)之后, finalPath是user
综合来看, 在预处理阶段

1.在调用中间件的时候, 是将某个路由的handler处理函数和中间件的处理函数都放在了Handlers的数组中

2.在调用Group的时候, 是将路由的path上面拼上Group的值. 也就是/user/:name, 会变成v1/user:name

6. 开始注册

// routergroup.go:L72-77
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {

    absolutePath := group.calculateAbsolutePath(relativePath) // <---    
    handlers = group.combineHandlers(handlers) // <---    
    group.engine.addRoute(httpMethod, absolutePath, handlers)    
    return group.returnObj()
   
 }

调用group.engine.addRoute(httpMethod, absolutePath, handlers)将预处理阶段的结果注册到gin Engine的trees上

7. gin路由树

gin的路由树算法是一棵前缀树. 不过并不是只有一颗树, 而是每种方法(POST, GET …)都有自己的一颗树

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    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)        
            engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    
    root.addRoute(path, handlers)
    
}
    
   type node struct {
       path      string    
       indices   string    
       children  []*node    
       handlers  HandlersChain    
       priority  uint32    
       nType     nodeType    
       maxParams uint8    
       wildChild bool
   }

其实gin的实现不像一个真正的树, children []*node所有的孩子都放在这个数组里面,
利用indices, priority变相实现一棵树

8. 获取路由handler

当服务端收到客户端的请求时, 根据path去trees匹配到相关的路由, 拿到相关的处理handlers

t := engine.treesfor i, tl := 0, len(t); i < tl; i++ {
    if t[i].method != httpMethod {
            continue
    }
    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
}

主要在下面这个函数里面调用程序注册的路由处理函数

func (c *Context) Next() {

    c.index++    
    for c.index < int8(len(c.handlers)) {
            c.handlers[c.index](c)        
            c.index++
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值