gin 框架
1 简介
- Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
- 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的
net/http
足够简单,性能也非常不错 - 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范
1.1 安装
要安装Gin软件包,您需要安装Go并首先设置Go工作区。
1.首先需要安装Go(需要1.10+版本),然后可以使用下面的Go命令安装Gin。
go get -u github.com/gin-gonic/gin
2.将其导入您的代码中:
import “github.com/gin-gonic/gin”
3.(可选)导入net/http。例如,如果使用常量,则需要这样做http.StatusOK。
import “net/http”
1.2 hello gin
package main
import "github.com/gin-gonic/gin"
func main() {
// 1.创建一个默认路由引擎(实例化gin.Engine结构体对象)
// gin.Default() 返回的就是一个 *Engine 结构体
r := gin.Default()
// 2.绑定路由规则,执行的函数
r.GET("/", func(c *gin.Context) { // gin.Context,封装了request和response
// string 第二个参数类似于 fmt.printf
c.String(200, "值:%v", "hello gin")
})
// 启动 gin 默认端口 8080,也可以通过自定义
r.Run()
}
gin 可开启多个路由引擎
package main
import "github.com/gin-gonic/gin"
func main() {
// 创建一个默认路由引擎
r := gin.Default()
// 配置路由
r.GET("/", func(c *gin.Context) {
c.String(200, "值:%v", "hello gin")
})
// 开启第二个路由
r.GET("/new", func(c *gin.Context) {
c.String(200, "new")
})
// 启动 gin
r.Run()
}
1.3 gin 工作流程
- Engine 容器对象,整个框架的基础
- Engine.trees 负责存储路由和handle方法的映射,采用类似字典树的结构
- Engine.RouterGroup ,其中的Handlers存储着所有中间件
- Context 上下文对象,负责处理 请求和回应 ,其中的 handlers 是存储处理请求时中间件和处理方法的
1.4 请求生命周期
客户端请求流程如下:
- 客户端向 gin 服务器端发起请求,然后通过中间件将数据传递给路由系统
- 所谓的路由系统其实就是我们在代码中定义的 url 路径
- 达到了 url 路由系统之后将有视图函数进行反馈渲染 html 或者 css 模板
- 向数据库发送用户想要查询的操作,如 增删改查 等
1.5 Gin源码
1.5.1 gin.Default()
Default() 跟 New() 几乎一模一样, 就是调用了 gin 内置的 Logger(), Recovery() 中间件
// Default返回一个已经附加了Logger和Recovery中间件的Engine实例
func Default() *Engine {
debugPrintWARNINGDefault()
// 默认初始化一个结构体,类似于工厂函数
engine := New()
// 注册中间件,中间件的是一个函数,最终只要返回一个 type HandlerFunc func(*Context) 就可以
engine.Use(Logger(), Recovery()) // 默认注册的两个中间件,分别是 Logger、Recovery
return engine
}
也就是说在 gin 框架中很多的功能实现都是基于 Engine 结构体来实现,
1.5.2 engine := New()
初始化
通过调用 gin.New()
方法来实例化 Engine
结构体
-
初始化了 Engine ,因为
func Default() *Engine {}
默认返回的就是一个 engine -
将 RouterGroup 的 Handlers(数组) 设置成 nil , basePath(基础路由) 设置成 /
-
为了使用方便, RouteGroup 里面也有一个 Engine 指针, 这里将刚刚初始化的engine赋值给了 RouterGroup 的engine 指针
-
为了防止频繁的context GC造成效率的降低, 在Engine 里使用了 sync.Pool , 专门存储 gin 的 Context
为了使用方便 New() 函数默认返回 *Engine 指针,从而实现数据的初始化实例,然后再将路由保存到了 methodTrees
(访问路径的树形结构,比如 RUL 默认是 / 开始,而且在 / 下面有多级子目录的这么一个结构),其实这里的 methodTrees
就是用来实现匹配路由规则的,也就是说当用户访问到某一级路由之后从而实现对应的处理函数,这就是 methodTrees
的作用
1.5.3 engine.Use() 注册中间件
其实中间件就是当我们进行路由匹配之前我们需要提前执行的这么几个函数而已。
- gin 框架中的中间件设计很巧妙,我们可以首先从我们最常用的
r := gin.Default()
的Default
函数开始看 - 它内部构造一个新的
engine
之后就通过Use()
函数注册了Logger(传递日志)
中间件和Recovery(捕获异常)
中间件 Use()
就是gin
的引入中间件的入口了.- 仔细分析这个函数, 不难发现
Use()
其实是在给RouteGroup
引入中间件的. - 具体是如何让中间件在
RouteGroup
上起到作用的, 等说到RouteGroup
再具体说.
其中
c.Next()
就是很关键的一步,它的代码很简单
-
从下面的代码可以看到,这里通过索引遍历
HandlersChain
链条 -
从而实现依次调用该路由的每一个函数(中间件或处理请求的函数)
-
我们可以在中间件函数中通过再次调用
c.Next()
实现嵌套调用(func1中调用func2;func2中调用func3)
1.5.4 注册路由
r.GET("/", func(ctx *gin.Context) {
ctx.String(200, "hahah")
})
通过Get方法将路由和处理视图函数注册
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)
// ---- 调用 addRoute 方法注册路由
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
- addRoute构造路由树
- 这段代码就是利用method, path, 将handlers注册到engine的trees中.
- 注意这里为什么是HandlersChain呢, 可以简单说一下, 就是将中间件和处理函数都注册到 method, path 的 tree 中了
也就是当我们访问对应的 URL 的时候就会触发路由树并匹配对应的处理函数进行处理
1.5.5 r.run() 启动服务
- 通过调用 net/http 来启动服务,由于 engine 实现了 ServeHTTP 方法
- 只需要直接传 engine 对象就可以完成初始化并启动
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)
// 在golang中,你要构建一个web服务,必然要用到http.ListenAndServe
// 第二个参数必须要有一个handler
err = http.ListenAndServe(address, engine.Handler())
return
}
所以通过查看源码可以看到 gin 框架本身就是对 HTTP 做了一个封装,并且用于处理他自身的 gin engine