既然go原生提供了http功能,为啥还需要gin等第三方框架呢?
主要还是原生的http不满足实际业务场景的需求,gin主要提供了以下额外的功能:
redix tree
实现路由功能,相比于普通前缀树来说树高度更小,占用内存更小,速度更快下面是官方的qucikstart:
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET( "/ping" , func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message" : "pong" ,
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
需要做3件事:
r := gin.Default()
func Default() *Engine {
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
继续看New():
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
root: true,
trees: make(methodTrees, 0, 9),
},
// ...
}
engine.RouterGroup.engine = engine
engine.pool.New = func() any {
return engine.allocateContext()
}
return engine
}
Engine有几个关键字段:
接着看engine.Use(Logger(), Recovery())
:
添加两个全局中间件:
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
// ...
return engine
}
添加到group.Handlers中
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
其中HandlerFunc类型为:
type HandlerFunc func(*Context)
实际处理请求时没有区分中间件和业务处理函数,其函数签名都为HandlerFunc,
入口为:
r.GET( "/ping" , func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message" : "pong" ,
})
})
跟进去看:
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
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()
}
流程为:
将绝对路径和对应的处理器链,注册到路由树上
入口为:
r.Run()
func (engine *Engine) Run(addr ...string) (err error)
address := resolveAddress(addr)
err = http.ListenAndServe(address, engine.Handler())
return
}
关于go原生的http.ListenAndServe可以看 Golang http请求源码解读
最终请求会进入到Engine的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)
}
handleHTTPRequest:
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
rPath := c.Request.URL.Path
// 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
value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.params != nil {
c.Params = *value.params
}
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
}
}