简介
- Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
- 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的
net/http足够简单,性能也非常不错 - 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范
特性
-
快速
- 基于Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
-
支持中间件
- 传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
-
Crash处理
- Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。
-
JSON验证
- Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
-
路由组
- 更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
-
错误管理
- Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
-
内置渲染
- Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
-
可扩展性
- 新建一个中间件非常简单
路由注册流程
核心数据结构
gin.Engine
该结构中包含三个核心对象:
- RouterGroup: 路由组,和路由管理相关
- 路由树数组trees: 标准库本身的路由是不区分请求方法的,也就是说注册一个路由后,GET、POST都能匹配到该路由。这显然不是我们想要的,我们希望的是同一个路由在不同的请求方法下,由不同的逻辑进行处理。其实就是通过路由树实现的,gin的针对每个请求方法都有一棵路由树
- context对象池: gin.Context是gin框架暴露给开发的另一个核心对象,可以通过该对象获取请求信息,业务处理的结果也是通过该对象写回客户端的。为了实现context对象的复用,gin基于sync.Pool实现了对象池
RouterGroup
路由组的目的是为了实现配置的复用。
比如有一组对food的请求: /food/add、/food/query、/food/update等,我们希望在注册路由时尽量简单(不要每次都写/food),并且与food相关的请求使用一组单独的middleware(与其他对象的请求隔离开),这时候就可以使用路由组。
type RouterGroup struct {
// 路由组处理函数链,其下路由的函数链将结合路由组和自身的函数组成最终的函数链
Handlers HandlersChain
// 路由组的基地址,一般是其下路由的公共地址
basePath string
// 路由组所属的Engine,这里构成了双向引用
engine *Engine
// 该路由组是否位于根节点,基于RouterGroup.Group创建路由组时此属性为false
root bool
}
HandlersChain处理器链
用于收集该路由组下注册的middleware函数,在运行时,会按顺序执行HandlersChain中的注册的函数。
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc slice.
// NOTE: 路由处理函数链,运行时会根据索引先后顺序依次调用
type HandlersChain []HandlerFunc
执行流程
一般情况下使用gin框架开发时使用默认的engine即可,因为相对于直接使用gin.New()创建Engine对象,它只是多注册了两个中间件。 一般流程如下:
- 创建并初始化Engine对象
- 注册middleware
- 注册路由及处理函数
- 服务端口监听
请求处理
对请求处理入口就是Engine.ServerHTTP方法
其核心处理逻辑如下:
- 从context对象池取一个可用的context对象,后续交互就是靠这个对象完成的
- 将
http.ResponseWriter和http.Request对象保存到context中。我们通过context获取请求参数、写入响应,其实是因为其底层封装了这两个对象的方法 - 调用Engine的
handleHTTPRequest方法,对请求进行处理。注意到其参数已经变成了gin.Context了。 - 请求处理完毕,回收context,以便下次复用。
请求绑定和响应渲染
-
请求绑定
- gin提供了一系列的方式,用于从请求中获取参数和数据等信息
-
响应渲染
- 就是将响应结构体序列化为字节数据,再写入
http.ResponseWriter中
- 就是将响应结构体序列化为字节数据,再写入