### 参考资料
* [Gin 源码深度解析及实现](https://blog.csdn.net/weixin_45177370/article/details/135295839)
* [非常简易版的 gin](https://github.com/gobylor/gin-read/blob/main/simplified_gin/lorgin/gin.go)

### 分析过程
先创建一个简单的demo，执行后的调用链路在[这里](https://5xfd05tkng.execute-api.cn-northwest-1.amazonaws.com.cn/callstack?demo=202406/665d6d3768e3f0.94054860.gz)
```go
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(":8000")
}
```
运行后，请求一下127.0.0.1:8080/ping 最终的调用链路如下：
```
|-goroutine-1 created by runtime.main
  |-main.main
    |-github.com/gin-gonic/gin.Default
      |-engine.Use(Logger(), Recovery())                   // 注册了两个中间件 
    |-github.com/gin-gonic/gin.(*RouterGroup).handle
    |-github.com/gin-gonic/gin.(*Engine).Run
      |-github.com/gin-gonic/gin.resolveAddress
      |-net/http.ListenAndServe(address, engine.Handler()) // 这里最终还是用的net/http包起的服务
                                                           // 不同的是serverMux使用的自定义的
                                                           // engine.Handler()

|-goroutine-7 created by net/http.(*Server).Serve.func3
  |-net/http.(*conn).serve
    |-net/http.(*conn).readRequest
    |-net/http.serverHandler.ServeHTTP
      |-github.com/gin-gonic/gin.(*Engine).ServeHTTP
        |-sync.(*Pool).Get                                  // 循环利用context对象内存
        |-github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(c)
          |-root := engine.trees[i].root                    // 匹配对应请求类型的tree
          |-value := root.getValue(rPath, c.params, ...)    // 匹配对应路由的handler
          |-c.handlers = value.handlers                     // 设置中间件
          |-c.Next()                                        // 执行第一个中间件
          |-github.com/gin-gonic/gin.LoggerWithConfig.func1
            |-github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1
              |-main.main.func1
                |-github.com/gin-gonic/gin.(*Context).Query // 获取get参数
                  |-github.com/gin-gonic/gin.(*Context).GetQueryArray
                    |-net/url.(*URL).Query                  // 首次调用需要实例一个map对象
                      |-net/url.parseQuery
        |-sync.(*Pool).Put
    |-net/http.(*response).finishRequest
```
结合[上一篇的文章](https://learnku.com/articles/85539)，我们可以发现gin的服务器流程和net/http包的差不多，只是在响应连接的时候使用了自己的serverMux。gin号称自己是内存零分配路由，那么是否真的是零分配呢？

其实主要是看gin.(*Engine).handleHTTPRequest()在匹配路由时是否使用了额外内存，主要看`root.getValue(rPath, c.params, c.skippedNodes, unescape)`，其中：

* rPath等于c.Request.URL.Path，这是在net/http创建的。
* c.params和c.skippedNodes是`gin.(*Engine).ServeHTTP()`用sync.Pool创建的，具体的New方法参考`gin.(*Engine).allocateContext()`。所以也可以认为是零分配。

因此可以说gin在处理请求时，匹配路由的部分是零分配的。