从运行服务看底层原理 启动监听8080端口的服务
1 2 3 4 5 6 7 8 func main () { router := gin.New() router.GET("users" , func (c *gin.Context) { ... }) router.Run(":8080" ) }
在router.Run()中可以看到,它本质上是调用go原生http包的ListenAndServe方法,gin的Engine本质上是一个路由处理器,其内部定义了路由处理的各种规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (engine *Engine) Run(addr ...string ) (err error ) { defer func () { debugPrintError(err) }() if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details." ) } address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n" , address) err = http.ListenAndServe(address, engine.Handler()) return }
而Engine结构体实现了http.Handler接口的ServeHTTP方法,请求会首先进入到ServeHTTP函数进行处理,具体处理流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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) }
处理请求 在上面的第三步中,engine.handleHTTPRequest(c)
开始处理请求,请求过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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) } t := engine.trees for i, tl := 0 , len (t); i < tl; i++ { if t[i].method != httpMethod { continue } root := t[i].root 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 } if httpMethod != http.MethodConnect && 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 , c.skippedNodes, unescape); value.handlers != nil { c.handlers = engine.allNoMethod serveError(c, http.StatusMethodNotAllowed, default405Body) return } } } c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body) }
中间件 以上第三步执行的c.Next()
实际上是逐个调用处理器处理请求,而中间件也是处理器类型 。
1 2 3 4 5 6 7 8 9 func (c *Context) Next() { c.index++ for c.index < int8 (len (c.handlers)) { c.handlers[c.index](c) c.index++ } }
注册中间件 可以创建多个路由组,不同的路由组可以有不同的中间件
1 2 3 4 5 6 7 8 r.Use(gin.Recovery()) r.Use(middleware.Logger()) r.Use(middleware.Cors()) auth := r.Group("api/v1" ) auth.Use(middleware.JwtToken())
1 2 3 4 5 6 7 func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { engine.RouterGroup.Use(middleware...) engine.rebuild404Handlers() engine.rebuild405Handlers() return engine }
中间件HandlerFunc是一个函数指针type HandlerFunc func(*Context)
Engine对象初始化 1 router := gin.New()或者router := gin.Default()
gin.New()
和 gin.Default()
区别是后者在前者的基础上添加了默认中间件。
gin.New()
的逻辑如下:
1、初始化Engine对象
2、设置sync.pool的新建上下文对象的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func New () *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ Handlers: nil , basePath: "/" , root: true , }, FuncMap: template.FuncMap{}, RedirectTrailingSlash: true , RedirectFixedPath: false , HandleMethodNotAllowed: false , ForwardedByClientIP: true , RemoteIPHeaders: []string {"X-Forwarded-For" , "X-Real-IP" }, TrustedPlatform: defaultPlatform, UseRawPath: false , RemoveExtraSlash: false , UnescapePathValues: true , MaxMultipartMemory: defaultMultipartMemory, trees: make (methodTrees, 0 , 9 ), delims: render.Delims{Left: "{{" , Right: "}}" }, secureJSONPrefix: "while(1);" , trustedProxies: []string {"0.0.0.0/0" , "::/0" }, trustedCIDRs: defaultTrustedCIDRs, } engine.RouterGroup.engine = engine engine.pool.New = func () any { return engine.allocateContext() } return engine }
创建路由组和注册路由 1 2 auth := r.Group("api/v1" ) auth.GET("users" , v1.GetUsers)
r.Group()
内部逻辑:
1 2 3 4 5 6 7 8 9 func (group *RouterGroup) Group(relativePath string , handlers ...HandlerFunc) *RouterGroup { return &RouterGroup{ Handlers: group.combineHandlers(handlers), basePath: group.calculateAbsolutePath(relativePath), engine: group.engine, } }
所以创建路由组,仅是返回路由组对象,省去用户填写相同路径前缀和中间件。
auth.GET()
内部逻辑:
1 2 3 4 5 6 7 8 9 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() }
上下文 gin的上下文和go原生的上下文不是同一个context
gin上下文方法分为:创建、流程控制、错误管理、元数据管理、请求数据、响应渲染、内容协商。
Context结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 type Context struct { writermem responseWriter Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 fullPath string engine *Engine params *Params skippedNodes *[]skippedNode mu sync.RWMutex Keys map [string ]any Errors errorMsgs Accepted []string queryCache url.Values formCache url.Values sameSite http.SameSite }
注意点 如果上下文Context被传递给协程,必须使用拷贝函数func (c *Context) Copy() *Context{}
,因为当一个请求被所有处理器处理完成后,Context会被存到内存池里,等待下一个请求复用该上下文,所以协程中的上下文必须是深拷贝的上下文。
路由树(radix树) 结构如下:
1 2 3 4 5 6 7 8 9 10 type node struct { path string indices string wildChild bool nType nodeType priority uint32 children []*node handlers HandlersChain fullPath string }