Gin Web 框架的使用和流程分析
Demo
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func HelloWorld(c *gin.Context) {
c.String(http.StatusOK, "pong")
}
func main() {
r := gin.Default() // 获取一个Gin的实例:Engine,gin的核心数据结构
r.Use(gin.Logger(), gin.Recovery()) // 使用中间键
r.GET("/ping", HelloWorld) // 注册路由
port := "6969"
log.Printf("Listening on port %s", port)
r.Run(":" + port) // 开始监听端口,提供服务
}
Gin 请求生命周期
Gin的数据结构
在原生的http 服务中,我们知道,只要实现了 Handler 接口就可以提供HTTP 服务,在gin中,他也实现了 Handler 接口 ,其实,就是因为gin 的 Engine 实现了 Handler 的接口,他就和 原生的 http 接口相关联了起来
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// ServeHTTP conforms to the http.Handler interface.
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)
}
type Engine struct {
RouterGroup // 路由组,注册的路径和业务处理函数就在这里
// ... 省略代码
pool sync.Pool // context Pool
trees methodTrees // 按照请求的HTTP method进行了分组
}
简单了解下 Gin 服务执行流程
Gin 生命周期
创建 Gin 实例
r := gin.Default() // 获取一个Gin的实例:Engine,gin的核心数据结构
这里获取了一个默认的 gin的实例
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New() // 默认实例
engine.Use(Logger(), Recovery()) // 注册中间建,中间件的是一个函数,最终只要返回一个 type HandlerFunc func(*Context) 就可以
return engine
}
```go
#### 路由注册
```go
r.GET("/", func(c *gin.Context) { // 注册路由, 实际上是调用了 RouterGroup 的Get方法, RouterGroup 实现了 IRoutes interface,RouterGroup 主要提供路有注册功能
c.String(http.StatusOK, "Hello World!")
})
// GET is a shortcut for router.Handle("GET", path, handle).
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) // 注意这个 combineHandlers,这里会把 r.Use(gin.Logger(), gin.Recovery()) 注册的中间键跟业务的处理函数组合在一起
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
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 mergedHandlers
}
// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
// ... 省略代码
}
接收请求并响应
// ServeHTTP conforms to the http.Handler interface.
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)
}
func (engine *Engine) handleHTTPRequest(c *Context) {
// ... 省略代码
// Find root of the tree for the given HTTP method
// x
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, 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
}
// ... 省略代码
}
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c) // 业务处理,也就是这里的 func HelloWorld(c *gin.Context), 也会跑中间键
c.index++
}
}
func HelloWorld(c *gin.Context) {
c.String(http.StatusOK, "pong")
}
如何写自己的中间键
可以参考 gin. Logger, 只要你的中间键,最后的返回结果是一个函数,函数类型为:HandlerFunc 就可以
func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
}
type HandlerFunc func(*Context)
Gin 整个流程如下图
如何和原生的 http 包结合起来
前面我们提到只要一个结构体只要实现了 Handler 接口就可以提供HTTP 服务,gin实现了 Handler 接口 。所以它可以和原生的 http 包结合起来。
整个流程就会变成如下图:
最底层的 net.Listener 在监听 TCP 段口,net库将接收到的请求给 http.Server , http.Server0最终会调用服务器实现的 ServerHTTP 函数,因为 gin 实现了 ServerHTTP 函数,所以最终处理请求会交给 gin 来处理