Gin 简介
Gin是一个轻量级的Web框架,用于构建高性能的Go语言Web应用程序。提供了路由管理、中间件支持、参数绑定和验证、错误处理、静态文件服务等功能。
Gin框架解决了什么问题和痛点
1.golang http 标准库本身提供了比较简单的路由注册能力,只支持精确匹配,而实际开发时难免会遇到需要使用通配、路径参数的场景。(gin可以使用路由组,来实现通配)
2 标准库需要我们手动从请求中读取数据、反序列化,响应时手动序列化、设置Content-Type、写响应内容,比较麻烦 (gin 使用 bind shouldBind)
3. 实际开发中, 我们对请求或响应进行一些前置或后置处理,接基于标准库开发,业务和非业务代码难免会耦合在一起 ,(gin可以使用中间件等 做一些统一的前置处理)
Gin快速和高性能的原因
轻量级设计:Gi只提供了一些基本的功能和组件,避免了不必要的复杂性。相比于其他框架,Gin的代码量更少,运行时的资源消耗也较低。
高效的路由引擎:Gin使用了高效的路由引擎,基于压缩前缀树(radix tree)和参数树(trie tree)实现了快速的路由匹配。这种路由匹配算法具有较低的时间复杂度。O(k),其中k是键的长度。
优化的上下文处理:Gin在处理请求时使用了上下文(Context)对象,它封装了请求和响应的相关信息。Gin对上下文对象进行了优化,采用了对象池(pool)技术,避免了频繁的对象创建和销毁操作,减少了内存分配的开销。
支持并发处理:Gin框架天生支持并发处理请求。每个请求都会在独立的goroutine中进行处理,这使得多个请求可以同时被处理,提高了并发处理能力。此外,Gin还提供了对goroutine的调度和管理,确保了请求的同步和顺序处理。
中间件机制: Gin的中间件机制允许开发者在请求到达处理函数之前或之后执行一些通用的逻辑。中间件可以用于身份验证、日志记录、错误处理等操作。由于中间件是按照顺序执行的,它们可以在请求处理链中灵活地插入和组合,而不会对性能造成明显的影响。
优化的JSON解析:Gin使用了快速的JSON解析库,如jsoniter,来提高JSON数据的解析速度。这对于处理大量的JSON请求和响应非常有利,可以减少CPU时间的消耗。
Gin使用流程
基于gin开发的一般流程可总结为:
1 创建并初始化Engine对象 gin.default()
2 注册middleware gin.use() (默认会注册Logger()和Recovery()这两个中间件函数) (gin.Next 可以添加自定义中间件)
3 注册路由 (路由组RouterGroup ,可以实现对路由分组,复用前缀 )
4 处理函数 (使用bind shouldbind解析传参->业务逻辑处理->返回结果)
5 服务端口监听
6 在mian函数中加入:gin.run()
Gin中间件
Gin框架中的中间件(Middleware)是一种用于在请求处理过程中执行通用逻辑的机制。中间件可以在请求处理之前或之后执行一些操作
使用中间件:
注册中间件:使用r.Use()方法将中间件注册到Gin的路由器实例上。
中间件顺序:注册的中间件函数会按照注册的顺序依次执行。
中间件的工作原理:
请求流程:当收到一个HTTP请求时,Gin框架会根据请求的URL和HTTP方法查找匹配的路由。如果有注册的中间件,Gin会将请求传递给中间件链。中间件链中的每个中间件函数都会在请求处理前执行
上下文传递:每个中间件函数都接收一个gin.Context参数,它包含了请求的上下文信息。中间件可以通过Context对象访问和操作请求和响应的数据、路由信息等。中间件可以使用c.Next()方法将请求传递给下一个中间件或路由处理函数,继续处理请求。
中间件分类
其中(Logger,Recovery中间件会默认注册)
Logger(日志记录中间件):Logger中间件用于记录请求和响应的详细信息,例如请求方法、路径、响应状态码等。
Recovery(恢复中间件):Recovery中间件用于在发生panic时恢复应用程序的正常运行。它会捕获并处理应用程序中的异常,防止应用程序崩溃,并返回一个恢复后的响应。
CORS(跨域资源共享中间件):CORS中间件用于处理跨域资源共享的问题。它可以设置响应头,允许跨域请求,并控制响应头中的CORS相关字段,例如Access-Control-Allow-Origin、Access-Control-Allow-Methods
Auth(身份验证中间件):Auth中间件用于验证请求的身份和权限。它可以检查请求中的身份凭证,例如JWT令牌或会话ID,并验证其有效性。
Rate Limiting(限流中间件):Rate Limiting中间件用于限制请求的频率和数量。它可以根据IP地址、用户ID或其他标识符来计算请求的速率,并拒绝超过限制的请求。
Cache(缓存中间件):Cache中间件用于缓存响应数据,以减少对后端服务的请求。它可以根据请求的URL或其他标识符来检查缓存,并返回缓存的响应,而不必重新计算或查询数据。
Gin.Engin
用来初始化一个gin对象实例,在该对象实例中主要包含了一些框架的基础功能,比如日志,中间件设置,路由控制(组),以及handlercontext等相关方法
type Engine struct {
// 路由组,在实际开发过程中我们通常会使用路由组来组织和管理一些列的路由. 比如: /apis/,/v1/等分组路由
RouterGroup
// 开启自动重定向。如果当前路由没有匹配到,但是存在不带/开头的handler就会重定向. 比如: 用户输入/foo/但是存在一个/foo 就会自动重定向到该handler,并且会向客户端返回301或者307状态码(区别在于GET方法和其他方法)
RedirectTrailingSlash bool
// 如果开启该参数,没有handler注册时,路由会尝试自己去修复当前的请求地址.
// 修复流程:
// 1.首位多余元素会被删除(../ or //); 2.然后路由会对新的路径进行不区分大小写的查找;3.如果能正常找到对应的handler,路由就会重定向到正确的handler上并返回301或者307.(比如: 用户访问/FOO 和 /..//Foo可能会被重定向到/foo这个路由上)
RedirectFixedPath bool
// 如果开启该参数,当当前请求不能被路由时,路由会自己去检查其他方法是否被允许.在这种情况下会响应"Method Not Allowed",并返回状态码405; 如果没有其他方法被允许,将会委托给NotFound的handler
HandleMethodNotAllowed bool
// 是否转发客户端ip
ForwardedByClientIP bool
// 如果开启将会在请求中增加一个以"X-AppEngine..."开头的header
AppEngine bool
// 如果开启将会使用url.RawPath去查找参数(默认:false)
UseRawPath bool
// 如果开启,请求路径将不会被转义. 如果UseRawPath为false,该参数实际上就为true(因为使用的是url.Path)
UnescapePathValues bool
// maxMemory参数的值(http.Request的ParseMultipartForm调用时的参数)
MaxMultipartMemory int64
// 是否删除额外的反斜线(开始时可解析有额外斜线的请求)
RemoveExtraSlash bool
// 分隔符(render.Delims表示使用HTML渲染的一组左右分隔符,具体可见html/template库)
delims render.Delims
// 设置在Context.SecureJSON中国的json前缀
secureJsonPrefix string
// 返回一个HTMLRender接口(用于渲染HTMLProduction和HTMLDebug两个结构体类型的模板)
HTMLRender render.HTMLRender
// html/template包中的FuncMap map[string]interface{} ,用来定义从名称到函数的映射
FuncMap template.FuncMap
// 以下是gin框架内部定义的一些属性
// HandlersChain 是一个HandlerFunc 的数组(HandlerFunc其实就是一个Context的指针,Context会在下一节讲解)
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
// 这里定义了一个可以临时存取对象的集合(sync.Pool是线程安全的,主要用来缓存为使用的item以减少GC压力,使得创建高效且线程安全的空闲队列)
pool sync.Pool
// methodTrees是methodTree的切片(methodTree是一个包含请求方法和node指针的结构体,node是一个管理path的节点树)
trees methodTrees
}
Gin.Context
: 在Gin框架中由Router结构体来负责路由和方法(URL和HTTP方法)的绑定,内的Handler采用Context结构体来处理具体的HTTP数据传输方式,比如HTTP头部,请求体参数,状态码以及响应体参数和其他的一些常见HTTP行为。
type Context struct {
writermem responseWriter
Request *http.Request // http请求
Writer ResponseWriter // http响应输出流
Params Params // URL路径参数
handlers HandlersChain // 处理器链
index int8 // 当前的处理进度,即处理链路处于函数链的索引位置
fullPath string
engine *Engine
...
mu sync.RWMutex // 用于保护 map 的读写互斥锁
// 提供对外暴露的 Get 和 Set 接口向用户提供了共享数据的存取服务,相关操作都在读写锁的保护之下,能够保证并发安全
Keys map[string]any // 缓存 handlers 链上共享数据的 map,由于使用的map,避免了设置多个值时context形成链表
...
queryCache url.Values // 查询参数缓存,使用时调用`Request.URL.Query()`,该方法每次都会对原始的查询字符串进行解析,所以这里设置缓存避免冗余的解析操作
formCache url.Values // 表单参数缓存,作用同上
...
}
前缀树和压缩前缀树
前缀树(Trie tre)
前缀树也称Trie树或字典树,是一种基于字符串公共前缀构建树形结构,来降低查询时间和提高效率的目的。前缀树一般用于统计和排序大量的字符串,其核心思想是空间换时间。
1根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2从根节点到某一节点路径上所有字符连接起来,就是该节点对应的字符串。
3每个节点任意子节点包含的字符都不相同。
压缩前缀树(Radix Tree)
压缩前缀树是一种更节省空间的前缀树。对于压缩前缀树的每个节点,
如果某一个节点是其父节点的唯一子节点,则会与父节点合并
gin框架就采用的是压缩前缀树实现。