go 语言 Gin Web 框架的实现原理探究

打印 上一主题 下一主题

主题 867|帖子 867|积分 2601

Gin 是一个用 Go (Golang) 编写的 Web 框架,性能极优,具有快速、支持中心件、crash处理、json验证、路由组、错误管理、内存渲染、可扩展性等特点。
官网地址:https://gin-gonic.com/
源码地址:https://github.com/gin-gonic/gin/tree/v1.10.0
参考视频:gin框架底层技能原理分析_哔哩哔哩_bilibili

一、net/http 及 gin 使用示例



  • 使用 net/http 创建 web 服务
  1. package main
  2. import (
  3.         "net/http"
  4. )
  5. func main() {
  6.         // 使用 net/http 创建 web 服务
  7.         // 注册路由
  8.         http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  9.                 w.Write([]byte("Hello World!"))
  10.         })
  11.         // 启动监听
  12.         http.ListenAndServe(":8080", nil)
  13. }
复制代码


  • 使用 gin 创建 web 服务
  1. package main
  2. import (
  3.         "net/http"
  4.         "github.com/gin-gonic/gin"
  5. )
  6. func main() {
  7.         // 初始化 engine
  8.         r := gin.Default()
  9.         // 注册路由
  10.         r.GET("/ping", func(c *gin.Context) {
  11.                 c.JSON(http.StatusOK, gin.H{
  12.                         "message": "pong",
  13.                 })
  14.         })
  15.         // 启动服务
  16.         r.Run()
  17. }
复制代码
二、gin.Engine数据结构

  1. type Engine struct {
  2.         RouterGroup  // 路由组
  3.         RedirectTrailingSlash bool
  4.         RedirectFixedPath bool
  5.         HandleMethodNotAllowed bool
  6.         ForwardedByClientIP bool
  7.         AppEngine bool
  8.         UseRawPath bool
  9.         UnescapePathValues bool
  10.         RemoveExtraSlash bool
  11.         RemoteIPHeaders []string
  12.         TrustedPlatform string
  13.         MaxMultipartMemory int64
  14.         // UseH2C enable h2c support.
  15.         UseH2C bool
  16.         ContextWithFallback bool
  17.         delims           render.Delims
  18.         secureJSONPrefix string
  19.         HTMLRender       render.HTMLRender
  20.         FuncMap          template.FuncMap
  21.         allNoRoute       HandlersChain
  22.         allNoMethod      HandlersChain
  23.         noRoute          HandlersChain
  24.         noMethod         HandlersChain
  25.     // 对象池,用来复用gin.Context
  26.         pool             sync.Pool
  27.     // 路由树,根据不同的httpmethod,以及不同的路由,拆分http请求及处理函数
  28.         trees            methodTrees     
  29.         maxParams        uint16
  30.         maxSections      uint16
  31.         trustedProxies   []string
  32.         trustedCIDRs     []*net.IPNet
  33. }
  34. type RouterGroup struct {
  35.         Handlers HandlersChain
  36.         basePath string
  37.         engine   *Engine
  38.         root     bool
  39. }
  40. type Pool struct {
  41.         noCopy noCopy
  42.         local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
  43.         localSize uintptr        // size of the local array
  44.         victim     unsafe.Pointer // local from previous cycle
  45.         victimSize uintptr        // size of victims array
  46.         // New optionally specifies a function to generate
  47.         // a value when Get would otherwise return nil.
  48.         // It may not be changed concurrently with calls to Get.
  49.         New func() any
  50. }
  51. type methodTree struct {
  52.         method string
  53.         root   *node
  54. }
  55. type methodTrees []methodTree
  56. type node struct {
  57.         path      string
  58.         indices   string
  59.         wildChild bool
  60.         nType     nodeType
  61.         priority  uint32
  62.         children  []*node // child nodes, at most 1 :param style node at the end of the array
  63.         handlers  HandlersChain
  64.         fullPath  string
  65. }
  66. type HandlersChain []HandlerFunc
复制代码
  1. // src/net/http/method.go
  2. // http method
  3. const (
  4.         MethodGet     = "GET"
  5.         MethodHead    = "HEAD"
  6.         MethodPost    = "POST"
  7.         MethodPut     = "PUT"
  8.         MethodPatch   = "PATCH" // RFC 5789
  9.         MethodDelete  = "DELETE"
  10.         MethodConnect = "CONNECT"
  11.         MethodOptions = "OPTIONS"
  12.         MethodTrace   = "TRACE"
  13. )
复制代码

三、gin 注册路由流程



  • 创建engine
  1. func Default(opts ...OptionFunc) *Engine {
  2.         debugPrintWARNINGDefault()
  3.         engine := New()
  4.         engine.Use(Logger(), Recovery())
  5.         return engine.With(opts...)
  6. }
  7. func New(opts ...OptionFunc) *Engine {
  8.         debugPrintWARNINGNew()
  9.         engine := &Engine{
  10.                 RouterGroup: RouterGroup{
  11.                         Handlers: nil,
  12.                         basePath: "/",
  13.                         root:     true,
  14.                 },
  15.                 FuncMap:                template.FuncMap{},
  16.                 RedirectTrailingSlash:  true,
  17.                 RedirectFixedPath:      false,
  18.                 HandleMethodNotAllowed: false,
  19.                 ForwardedByClientIP:    true,
  20.                 RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
  21.                 TrustedPlatform:        defaultPlatform,
  22.                 UseRawPath:             false,
  23.                 RemoveExtraSlash:       false,
  24.                 UnescapePathValues:     true,
  25.                 MaxMultipartMemory:     defaultMultipartMemory,
  26.                 // 9 棵路由压缩前缀树,对应 http 的 9 种方法
  27.         trees:                  make(methodTrees, 0, 9),
  28.                 delims:                 render.Delims{Left: "{{", Right: "}}"},
  29.                 secureJSONPrefix:       "while(1);",
  30.                 trustedProxies:         []string{"0.0.0.0/0", "::/0"},
  31.                 trustedCIDRs:           defaultTrustedCIDRs,
  32.         }
  33.         engine.RouterGroup.engine = engine
  34.         engine.pool.New = func() any {
  35.                 return engine.allocateContext(engine.maxParams)
  36.         }
  37.         return engine.With(opts...)
  38. }
复制代码


  • 注册MiddleWare
  1. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
  2.         engine.RouterGroup.Use(middleware...)
  3.         engine.rebuild404Handlers()
  4.         engine.rebuild405Handlers()
  5.         return engine
  6. }
  7. func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
  8.         group.Handlers = append(group.Handlers, middleware...)
  9.         return group.returnObj()
  10. }
复制代码


  • 注册handler
   以 http post 方法为例,注册 handler 方法调用顺序为 RouterGroup.POST -> RouterGroup.handle:
  

  • 拼接出待注册方法的完整路径 absolutePath
  • 拼接出待注册方法的完整函数处理链 handlers
  • 以 absolutePath 和 handlers 组成kv对添加到路由树中
  1. func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
  2.         return group.handle(http.MethodPost, relativePath, handlers)
  3. }
  4. func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
  5.         absolutePath := group.calculateAbsolutePath(relativePath)
  6.         handlers = group.combineHandlers(handlers)
  7.         group.engine.addRoute(httpMethod, absolutePath, handlers)
  8.         return group.returnObj()
  9. }
  10. func joinPaths(absolutePath, relativePath string) string {
  11.         if relativePath == "" {
  12.                 return absolutePath
  13.         }
  14.         finalPath := path.Join(absolutePath, relativePath)
  15.         if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' {
  16.                 return finalPath + "/"
  17.         }
  18.         return finalPath
  19. }
  20. func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
  21.         finalSize := len(group.Handlers) + len(handlers)
  22.         assert1(finalSize < int(abortIndex), "too many handlers")
  23.         mergedHandlers := make(HandlersChain, finalSize)
  24.         copy(mergedHandlers, group.Handlers)
  25.         copy(mergedHandlers[len(group.Handlers):], handlers)
  26.         return mergedHandlers
  27. }
  28. func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  29.         assert1(path[0] == '/', "path must begin with '/'")
  30.         assert1(method != "", "HTTP method can not be empty")
  31.         assert1(len(handlers) > 0, "there must be at least one handler")
  32.         debugPrintRoute(method, path, handlers)
  33.         root := engine.trees.get(method)
  34.         if root == nil {
  35.                 root = new(node)
  36.                 root.fullPath = "/"
  37.                 engine.trees = append(engine.trees, methodTree{method: method, root: root})
  38.         }
  39.         root.addRoute(path, handlers)
  40.         if paramsCount := countParams(path); paramsCount > engine.maxParams {
  41.                 engine.maxParams = paramsCount
  42.         }
  43.         if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
  44.                 engine.maxSections = sectionsCount
  45.         }
  46. }
  47. func (n *node) addRoute(path string, handlers HandlersChain) {
  48.         fullPath := path
  49.         n.priority++
  50.         // Empty tree
  51.         if len(n.path) == 0 && len(n.children) == 0 {
  52.                 n.insertChild(path, fullPath, handlers)
  53.                 n.nType = root
  54.                 return
  55.         }
  56.         parentFullPathIndex := 0
  57. walk:
  58.         for {
  59.                 // Find the longest common prefix.
  60.                 // This also implies that the common prefix contains no ':' or '*'
  61.                 // since the existing key can't contain those chars.
  62.                 i := longestCommonPrefix(path, n.path)
  63.                 // Split edge
  64.                 if i < len(n.path) {
  65.                         child := node{
  66.                                 path:      n.path[i:],
  67.                                 wildChild: n.wildChild,
  68.                                 nType:     static,
  69.                                 indices:   n.indices,
  70.                                 children:  n.children,
  71.                                 handlers:  n.handlers,
  72.                                 priority:  n.priority - 1,
  73.                                 fullPath:  n.fullPath,
  74.                         }
  75.                         n.children = []*node{&child}
  76.                         // []byte for proper unicode char conversion, see #65
  77.                         n.indices = bytesconv.BytesToString([]byte{n.path[i]})
  78.                         n.path = path[:i]
  79.                         n.handlers = nil
  80.                         n.wildChild = false
  81.                         n.fullPath = fullPath[:parentFullPathIndex+i]
  82.                 }
  83.                 // Make new node a child of this node
  84.                 if i < len(path) {
  85.                         path = path[i:]
  86.                         c := path[0]
  87.                         // '/' after param
  88.                         if n.nType == param && c == '/' && len(n.children) == 1 {
  89.                                 parentFullPathIndex += len(n.path)
  90.                                 n = n.children[0]
  91.                                 n.priority++
  92.                                 continue walk
  93.                         }
  94.                         // Check if a child with the next path byte exists
  95.                         for i, max := 0, len(n.indices); i < max; i++ {
  96.                                 if c == n.indices[i] {
  97.                                         parentFullPathIndex += len(n.path)
  98.                                         i = n.incrementChildPrio(i)
  99.                                         n = n.children[i]
  100.                                         continue walk
  101.                                 }
  102.                         }
  103.                         // Otherwise insert it
  104.                         if c != ':' && c != '*' && n.nType != catchAll {
  105.                                 // []byte for proper unicode char conversion, see #65
  106.                                 n.indices += bytesconv.BytesToString([]byte{c})
  107.                                 child := &node{
  108.                                         fullPath: fullPath,
  109.                                 }
  110.                                 n.addChild(child)
  111.                                 n.incrementChildPrio(len(n.indices) - 1)
  112.                                 n = child
  113.                         } else if n.wildChild {
  114.                                 // inserting a wildcard node, need to check if it conflicts with the existing wildcard
  115.                                 n = n.children[len(n.children)-1]
  116.                                 n.priority++
  117.                                 // Check if the wildcard matches
  118.                                 if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
  119.                                         // Adding a child to a catchAll is not possible
  120.                                         n.nType != catchAll &&
  121.                                         // Check for longer wildcard, e.g. :name and :names
  122.                                         (len(n.path) >= len(path) || path[len(n.path)] == '/') {
  123.                                         continue walk
  124.                                 }
  125.                                 // Wildcard conflict
  126.                                 pathSeg := path
  127.                                 if n.nType != catchAll {
  128.                                         pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
  129.                                 }
  130.                                 prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
  131.                                 panic("'" + pathSeg +
  132.                                         "' in new path '" + fullPath +
  133.                                         "' conflicts with existing wildcard '" + n.path +
  134.                                         "' in existing prefix '" + prefix +
  135.                                         "'")
  136.                         }
  137.                         n.insertChild(path, fullPath, handlers)
  138.                         return
  139.                 }
  140.                 // Otherwise add handle to current node
  141.                 if n.handlers != nil {
  142.                         panic("handlers are already registered for path '" + fullPath + "'")
  143.                 }
  144.                 n.handlers = handlers
  145.                 n.fullPath = fullPath
  146.                 return
  147.         }
  148. }
复制代码

四、gin 服务启动流程

   调用 *Engine.Run() 方法,底层会把 gin.Engine 本身作为 net/http 包下的 handler 接口的实现类,并调用 http.ListenAndServe 方法启动服务。
  1. func (engine *Engine) Run(addr ...string) (err error) {
  2.         defer func() { debugPrintError(err) }()
  3.         if engine.isUnsafeTrustedProxies() {
  4.                 debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  5.                         "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  6.         }
  7.         address := resolveAddress(addr)
  8.         debugPrint("Listening and serving HTTP on %s\n", address)
  9.         err = http.ListenAndServe(address, engine.Handler())
  10.         return
  11. }
  12. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  13.     // 从对象池获取一个 context
  14.         c := engine.pool.Get().(*Context)
  15.     // 重置或初始化 context
  16.         c.writermem.reset(w)
  17.         c.Request = req
  18.         c.reset()
  19.    
  20.     // 处理 http 请求
  21.         engine.handleHTTPRequest(c)
  22.     // 把 context 放回对象池
  23.         engine.pool.Put(c)
  24. }
  25. func (engine *Engine) handleHTTPRequest(c *Context) {
  26.         httpMethod := c.Request.Method
  27.         rPath := c.Request.URL.Path
  28.         unescape := false
  29.         if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
  30.                 rPath = c.Request.URL.RawPath
  31.                 unescape = engine.UnescapePathValues
  32.         }
  33.         if engine.RemoveExtraSlash {
  34.                 rPath = cleanPath(rPath)
  35.         }
  36.         // Find root of the tree for the given HTTP method
  37.         t := engine.trees
  38.         for i, tl := 0, len(t); i < tl; i++ {
  39.                 if t[i].method != httpMethod {
  40.                         continue
  41.                 }
  42.                 root := t[i].root
  43.                 // Find route in tree
  44.                 value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
  45.                 if value.params != nil {
  46.                         c.Params = *value.params
  47.                 }
  48.                 if value.handlers != nil {
  49.                         c.handlers = value.handlers
  50.                         c.fullPath = value.fullPath
  51.                         c.Next()
  52.                         c.writermem.WriteHeaderNow()
  53.                         return
  54.                 }
  55.                 if httpMethod != http.MethodConnect && rPath != "/" {
  56.                         if value.tsr && engine.RedirectTrailingSlash {
  57.                                 redirectTrailingSlash(c)
  58.                                 return
  59.                         }
  60.                         if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
  61.                                 return
  62.                         }
  63.                 }
  64.                 break
  65.         }
  66.         if engine.HandleMethodNotAllowed {
  67.                 // According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
  68.                 // containing a list of the target resource's currently supported methods.
  69.                 allowed := make([]string, 0, len(t)-1)
  70.                 for _, tree := range engine.trees {
  71.                         if tree.method == httpMethod {
  72.                                 continue
  73.                         }
  74.                         if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
  75.                                 allowed = append(allowed, tree.method)
  76.                         }
  77.                 }
  78.                 if len(allowed) > 0 {
  79.                         c.handlers = engine.allNoMethod
  80.                         c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
  81.                         serveError(c, http.StatusMethodNotAllowed, default405Body)
  82.                         return
  83.                 }
  84.         }
  85.         c.handlers = engine.allNoRoute
  86.         serveError(c, http.StatusNotFound, default404Body)
  87. }
复制代码
  Engine.handleHTTPRequest 处理 http 哀求的流程:
  

  • 根据 http method 取得对应的 methodTree
  • 根据 path 从 methodTree 中取得对应的 handlers 链
  • 将 handlers 链注入到 gin.context 中, 通过 context.Next() 方法遍历获取 handler
  

五、路由树原理

   1、前缀树
  又称Trie树、字典树或键树,是一种树形数据结构,主要用于高效地存储和查询字符串。
  其特点为:
  

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上颠末的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。
  

  
  2、压缩前缀树
  压缩前缀树是一种高效处理字符串聚集的数据结构,通过归并共享的前缀来节省存储空间并提高检索效率。它在网络路由、编译器符号表和字符串检索等领域有广泛应用。
  

  
  3、路由树的数据结构
  gin路由管理采取压缩前缀树的数据结构,相较于map数据结构,压缩前缀树可用于暗昧匹配,并且在数据量不大的情况下,压缩树的性能并不比map差。gin框架中采取补偿策略,将挂载的路径越多的子节点越往左排列,以便查询时优先被访问到。
  1. type methodTree struct {
  2.         method string
  3.         root   *node
  4. }
  5. type methodTrees []methodTree
  6. type node struct {
  7.         path      string
  8.     // 每个 indice 字符对应一个孩子节点路径的首字母
  9.         indices   string
  10.         wildChild bool
  11.         nType     nodeType
  12.     // 后续节点数量
  13.         priority  uint32
  14.     // 孩子节点列表
  15.         children  []*node // child nodes, at most 1 :param style node at the end of the array
  16.     // 处理函数链
  17.         handlers  HandlersChain
  18.         fullPath  string
  19. }
  20. type HandlersChain []HandlerFunc
复制代码
4、注册路由
  1. func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  2.         assert1(path[0] == '/', "path must begin with '/'")
  3.         assert1(method != "", "HTTP method can not be empty")
  4.         assert1(len(handlers) > 0, "there must be at least one handler")
  5.         debugPrintRoute(method, path, handlers)
  6.         root := engine.trees.get(method)
  7.         if root == nil {
  8.                 root = new(node)
  9.                 root.fullPath = "/"
  10.                 engine.trees = append(engine.trees, methodTree{method: method, root: root})
  11.         }
  12.         root.addRoute(path, handlers)
  13.         if paramsCount := countParams(path); paramsCount > engine.maxParams {
  14.                 engine.maxParams = paramsCount
  15.         }
  16.         if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
  17.                 engine.maxSections = sectionsCount
  18.         }
  19. }
  20. func (n *node) addRoute(path string, handlers HandlersChain) {
  21.         fullPath := path
  22.         n.priority++
  23.         // Empty tree
  24.         if len(n.path) == 0 && len(n.children) == 0 {
  25.                 n.insertChild(path, fullPath, handlers)
  26.                 n.nType = root
  27.                 return
  28.         }
  29.         parentFullPathIndex := 0
  30. walk:
  31.         for {
  32.                 // Find the longest common prefix.
  33.                 // This also implies that the common prefix contains no ':' or '*'
  34.                 // since the existing key can't contain those chars.
  35.                 i := longestCommonPrefix(path, n.path)
  36.                 // Split edge
  37.                 if i < len(n.path) {
  38.                         child := node{
  39.                                 path:      n.path[i:],
  40.                                 wildChild: n.wildChild,
  41.                                 nType:     static,
  42.                                 indices:   n.indices,
  43.                                 children:  n.children,
  44.                                 handlers:  n.handlers,
  45.                                 priority:  n.priority - 1,
  46.                                 fullPath:  n.fullPath,
  47.                         }
  48.                         n.children = []*node{&child}
  49.                         // []byte for proper unicode char conversion, see #65
  50.                         n.indices = bytesconv.BytesToString([]byte{n.path[i]})
  51.                         n.path = path[:i]
  52.                         n.handlers = nil
  53.                         n.wildChild = false
  54.                         n.fullPath = fullPath[:parentFullPathIndex+i]
  55.                 }
  56.                 // Make new node a child of this node
  57.                 if i < len(path) {
  58.                         path = path[i:]
  59.                         c := path[0]
  60.                         // '/' after param
  61.                         if n.nType == param && c == '/' && len(n.children) == 1 {
  62.                                 parentFullPathIndex += len(n.path)
  63.                                 n = n.children[0]
  64.                                 n.priority++
  65.                                 continue walk
  66.                         }
  67.                         // Check if a child with the next path byte exists
  68.                         for i, max := 0, len(n.indices); i < max; i++ {
  69.                                 if c == n.indices[i] {
  70.                                         parentFullPathIndex += len(n.path)
  71.                                         i = n.incrementChildPrio(i)
  72.                                         n = n.children[i]
  73.                                         continue walk
  74.                                 }
  75.                         }
  76.                         // Otherwise insert it
  77.                         if c != ':' && c != '*' && n.nType != catchAll {
  78.                                 // []byte for proper unicode char conversion, see #65
  79.                                 n.indices += bytesconv.BytesToString([]byte{c})
  80.                                 child := &node{
  81.                                         fullPath: fullPath,
  82.                                 }
  83.                                 n.addChild(child)
  84.                                 n.incrementChildPrio(len(n.indices) - 1)
  85.                                 n = child
  86.                         } else if n.wildChild {
  87.                                 // inserting a wildcard node, need to check if it conflicts with the existing wildcard
  88.                                 n = n.children[len(n.children)-1]
  89.                                 n.priority++
  90.                                 // Check if the wildcard matches
  91.                                 if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
  92.                                         // Adding a child to a catchAll is not possible
  93.                                         n.nType != catchAll &&
  94.                                         // Check for longer wildcard, e.g. :name and :names
  95.                                         (len(n.path) >= len(path) || path[len(n.path)] == '/') {
  96.                                         continue walk
  97.                                 }
  98.                                 // Wildcard conflict
  99.                                 pathSeg := path
  100.                                 if n.nType != catchAll {
  101.                                         pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
  102.                                 }
  103.                                 prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
  104.                                 panic("'" + pathSeg +
  105.                                         "' in new path '" + fullPath +
  106.                                         "' conflicts with existing wildcard '" + n.path +
  107.                                         "' in existing prefix '" + prefix +
  108.                                         "'")
  109.                         }
  110.                         n.insertChild(path, fullPath, handlers)
  111.                         return
  112.                 }
  113.                 // Otherwise add handle to current node
  114.                 if n.handlers != nil {
  115.                         panic("handlers are already registered for path '" + fullPath + "'")
  116.                 }
  117.                 n.handlers = handlers
  118.                 n.fullPath = fullPath
  119.                 return
  120.         }
  121. }
复制代码

  5、检索路由
  1. type nodeValue struct {
  2.         handlers HandlersChain
  3.         params   *Params
  4.         tsr      bool
  5.         fullPath string
  6. }
  7. type skippedNode struct {
  8.         path        string
  9.         node        *node
  10.         paramsCount int16
  11. }
  12. func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
  13.         var globalParamsCount int16
  14. walk: // Outer loop for walking the tree
  15.         for {
  16.                 prefix := n.path
  17.                 if len(path) > len(prefix) {
  18.                         if path[:len(prefix)] == prefix {
  19.                                 path = path[len(prefix):]
  20.                                 // Try all the non-wildcard children first by matching the indices
  21.                                 idxc := path[0]
  22.                                 for i, c := range []byte(n.indices) {
  23.                                         if c == idxc {
  24.                                                 //  strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
  25.                                                 if n.wildChild {
  26.                                                         index := len(*skippedNodes)
  27.                                                         *skippedNodes = (*skippedNodes)[:index+1]
  28.                                                         (*skippedNodes)[index] = skippedNode{
  29.                                                                 path: prefix + path,
  30.                                                                 node: &node{
  31.                                                                         path:      n.path,
  32.                                                                         wildChild: n.wildChild,
  33.                                                                         nType:     n.nType,
  34.                                                                         priority:  n.priority,
  35.                                                                         children:  n.children,
  36.                                                                         handlers:  n.handlers,
  37.                                                                         fullPath:  n.fullPath,
  38.                                                                 },
  39.                                                                 paramsCount: globalParamsCount,
  40.                                                         }
  41.                                                 }
  42.                                                 n = n.children[i]
  43.                                                 continue walk
  44.                                         }
  45.                                 }
  46.                                 if !n.wildChild {
  47.                                         // If the path at the end of the loop is not equal to '/' and the current node has no child nodes
  48.                                         // the current node needs to roll back to last valid skippedNode
  49.                                         if path != "/" {
  50.                                                 for length := len(*skippedNodes); length > 0; length-- {
  51.                                                         skippedNode := (*skippedNodes)[length-1]
  52.                                                         *skippedNodes = (*skippedNodes)[:length-1]
  53.                                                         if strings.HasSuffix(skippedNode.path, path) {
  54.                                                                 path = skippedNode.path
  55.                                                                 n = skippedNode.node
  56.                                                                 if value.params != nil {
  57.                                                                         *value.params = (*value.params)[:skippedNode.paramsCount]
  58.                                                                 }
  59.                                                                 globalParamsCount = skippedNode.paramsCount
  60.                                                                 continue walk
  61.                                                         }
  62.                                                 }
  63.                                         }
  64.                                         // Nothing found.
  65.                                         // We can recommend to redirect to the same URL without a
  66.                                         // trailing slash if a leaf exists for that path.
  67.                                         value.tsr = path == "/" && n.handlers != nil
  68.                                         return value
  69.                                 }
  70.                                 // Handle wildcard child, which is always at the end of the array
  71.                                 n = n.children[len(n.children)-1]
  72.                                 globalParamsCount++
  73.                                 switch n.nType {
  74.                                 case param:
  75.                                         // fix truncate the parameter
  76.                                         // tree_test.go  line: 204
  77.                                         // Find param end (either '/' or path end)
  78.                                         end := 0
  79.                                         for end < len(path) && path[end] != '/' {
  80.                                                 end++
  81.                                         }
  82.                                         // Save param value
  83.                                         if params != nil {
  84.                                                 // Preallocate capacity if necessary
  85.                                                 if cap(*params) < int(globalParamsCount) {
  86.                                                         newParams := make(Params, len(*params), globalParamsCount)
  87.                                                         copy(newParams, *params)
  88.                                                         *params = newParams
  89.                                                 }
  90.                                                 if value.params == nil {
  91.                                                         value.params = params
  92.                                                 }
  93.                                                 // Expand slice within preallocated capacity
  94.                                                 i := len(*value.params)
  95.                                                 *value.params = (*value.params)[:i+1]
  96.                                                 val := path[:end]
  97.                                                 if unescape {
  98.                                                         if v, err := url.QueryUnescape(val); err == nil {
  99.                                                                 val = v
  100.                                                         }
  101.                                                 }
  102.                                                 (*value.params)[i] = Param{
  103.                                                         Key:   n.path[1:],
  104.                                                         Value: val,
  105.                                                 }
  106.                                         }
  107.                                         // we need to go deeper!
  108.                                         if end < len(path) {
  109.                                                 if len(n.children) > 0 {
  110.                                                         path = path[end:]
  111.                                                         n = n.children[0]
  112.                                                         continue walk
  113.                                                 }
  114.                                                 // ... but we can't
  115.                                                 value.tsr = len(path) == end+1
  116.                                                 return value
  117.                                         }
  118.                                         if value.handlers = n.handlers; value.handlers != nil {
  119.                                                 value.fullPath = n.fullPath
  120.                                                 return value
  121.                                         }
  122.                                         if len(n.children) == 1 {
  123.                                                 // No handle found. Check if a handle for this path + a
  124.                                                 // trailing slash exists for TSR recommendation
  125.                                                 n = n.children[0]
  126.                                                 value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/")
  127.                                         }
  128.                                         return value
  129.                                 case catchAll:
  130.                                         // Save param value
  131.                                         if params != nil {
  132.                                                 // Preallocate capacity if necessary
  133.                                                 if cap(*params) < int(globalParamsCount) {
  134.                                                         newParams := make(Params, len(*params), globalParamsCount)
  135.                                                         copy(newParams, *params)
  136.                                                         *params = newParams
  137.                                                 }
  138.                                                 if value.params == nil {
  139.                                                         value.params = params
  140.                                                 }
  141.                                                 // Expand slice within preallocated capacity
  142.                                                 i := len(*value.params)
  143.                                                 *value.params = (*value.params)[:i+1]
  144.                                                 val := path
  145.                                                 if unescape {
  146.                                                         if v, err := url.QueryUnescape(path); err == nil {
  147.                                                                 val = v
  148.                                                         }
  149.                                                 }
  150.                                                 (*value.params)[i] = Param{
  151.                                                         Key:   n.path[2:],
  152.                                                         Value: val,
  153.                                                 }
  154.                                         }
  155.                                         value.handlers = n.handlers
  156.                                         value.fullPath = n.fullPath
  157.                                         return value
  158.                                 default:
  159.                                         panic("invalid node type")
  160.                                 }
  161.                         }
  162.                 }
  163.                 if path == prefix {
  164.                         // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
  165.                         // the current node needs to roll back to last valid skippedNode
  166.                         if n.handlers == nil && path != "/" {
  167.                                 for length := len(*skippedNodes); length > 0; length-- {
  168.                                         skippedNode := (*skippedNodes)[length-1]
  169.                                         *skippedNodes = (*skippedNodes)[:length-1]
  170.                                         if strings.HasSuffix(skippedNode.path, path) {
  171.                                                 path = skippedNode.path
  172.                                                 n = skippedNode.node
  173.                                                 if value.params != nil {
  174.                                                         *value.params = (*value.params)[:skippedNode.paramsCount]
  175.                                                 }
  176.                                                 globalParamsCount = skippedNode.paramsCount
  177.                                                 continue walk
  178.                                         }
  179.                                 }
  180.                                 //        n = latestNode.children[len(latestNode.children)-1]
  181.                         }
  182.                         // We should have reached the node containing the handle.
  183.                         // Check if this node has a handle registered.
  184.                         if value.handlers = n.handlers; value.handlers != nil {
  185.                                 value.fullPath = n.fullPath
  186.                                 return value
  187.                         }
  188.                         // If there is no handle for this route, but this route has a
  189.                         // wildcard child, there must be a handle for this path with an
  190.                         // additional trailing slash
  191.                         if path == "/" && n.wildChild && n.nType != root {
  192.                                 value.tsr = true
  193.                                 return value
  194.                         }
  195.                         if path == "/" && n.nType == static {
  196.                                 value.tsr = true
  197.                                 return value
  198.                         }
  199.                         // No handle found. Check if a handle for this path + a
  200.                         // trailing slash exists for trailing slash recommendation
  201.                         for i, c := range []byte(n.indices) {
  202.                                 if c == '/' {
  203.                                         n = n.children[i]
  204.                                         value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
  205.                                                 (n.nType == catchAll && n.children[0].handlers != nil)
  206.                                         return value
  207.                                 }
  208.                         }
  209.                         return value
  210.                 }
  211.                 // Nothing found. We can recommend to redirect to the same URL with an
  212.                 // extra trailing slash if a leaf exists for that path
  213.                 value.tsr = path == "/" ||
  214.                         (len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
  215.                                 path == prefix[:len(prefix)-1] && n.handlers != nil)
  216.                 // roll back to last valid skippedNode
  217.                 if !value.tsr && path != "/" {
  218.                         for length := len(*skippedNodes); length > 0; length-- {
  219.                                 skippedNode := (*skippedNodes)[length-1]
  220.                                 *skippedNodes = (*skippedNodes)[:length-1]
  221.                                 if strings.HasSuffix(skippedNode.path, path) {
  222.                                         path = skippedNode.path
  223.                                         n = skippedNode.node
  224.                                         if value.params != nil {
  225.                                                 *value.params = (*value.params)[:skippedNode.paramsCount]
  226.                                         }
  227.                                         globalParamsCount = skippedNode.paramsCount
  228.                                         continue walk
  229.                                 }
  230.                         }
  231.                 }
  232.                 return value
  233.         }
  234. }
复制代码

  六、gin.Context数据结构

  1. type Context struct {
  2.         writermem responseWriter
  3.         Request   *http.Request
  4.         Writer    ResponseWriter
  5.         Params   Params
  6.         handlers HandlersChain
  7.         index    int8
  8.         fullPath string
  9.         engine       *Engine
  10.         params       *Params
  11.         skippedNodes *[]skippedNode
  12.         // This mutex protects Keys map.
  13.         mu sync.RWMutex
  14.         // Keys is a key/value pair exclusively for the context of each request.
  15.         Keys map[string]any
  16.         // Errors is a list of errors attached to all the handlers/middlewares who used this context.
  17.         Errors errorMsgs
  18.         // Accepted defines a list of manually accepted formats for content negotiation.
  19.         Accepted []string
  20.         // queryCache caches the query result from c.Request.URL.Query().
  21.         queryCache url.Values
  22.         // formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
  23.         // or PUT body parameters.
  24.         formCache url.Values
  25.         // SameSite allows a server to define a cookie attribute making it impossible for
  26.         // the browser to send this cookie along with cross-site requests.
  27.         sameSite http.SameSite
  28. }
复制代码
  gin.Context 作为处理 http 哀求的通用数据结构,不可克制地会被频仍创建和销毁。为了缓解GC 压力,gin 中采取对象池sync.Pool进行Context 的缓存复用,处理流程如下:
  

  • http 哀求到达时,从 pool 中获取 Context,倘若池子已空,通过 pool.New 方法构造新的 Context补上空缺
  • http 哀求处理完成后,将Context 放回 pool 中,用以后续复用
  1. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  2.     // 从对象池获取一个 context
  3.         c := engine.pool.Get().(*Context)
  4.     // 重置或初始化 context
  5.         c.writermem.reset(w)
  6.         c.Request = req
  7.         c.reset()
  8.    
  9.     // 处理 http 请求
  10.         engine.handleHTTPRequest(c)
  11.     // 把 context 放回对象池
  12.         engine.pool.Put(c)
  13. }
复制代码
 
七、总结

   

  • gin 将 Engine 作为 http.Handler 的实现类进行注入,从而融入 Golang net/http 标准库的框架之内
  • gin 中基于 handler 链的方式实现中心件和处理函数的协调使用
  • gin 中基于压缩前缀树的方式作为路由树的数据结构,对应于9种 http 方法共有 9 棵树
  • gin 中基于 gin.Context 作为一次 http 哀求贯穿整条 handler chain 的核心数据结构
  • gin.Context 是一种会被频仍创建销毁的资源对象,因此使用对象池 sync.Pool 进行缓存复用
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表