go-web框架Gin特性介绍及实践(一)
1、gin简介
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
Gin 是一款由 golang 开发的HTTP Web 框架,它具有类似Martini 的 API 同时拥有更高的性能 ———— 速度提升40倍。如果您需要出色的性能,请使用Gin。
以上是Gin官方对该web框架的定位。截止目前为止,gin项目已经拥有 66.5k star 和 113.4k user,作为时下最流行的 高性能 go web 框架,gin 具有以下关键特征:
- 高性能(得益于httprouter,基于Radix树的路由、内存占用小、无反射)
- 中间件(灵活、可扩展)
- 无崩溃(内置异常抓取和恢复机制,服务始终可用)
- JSON 校验(方便解析并校验请求中的Json)
- 路由分组 (支持分组配置,支持嵌套而不降低性能)
- 错误管理
- 内置渲染(为JSON、XML 和 HTML 渲染提供了API)
- 可扩展 (自定义中间件)
2、快速开始
(1)、安装和更新
安装和更新
go get -u github.com/gin-gonic/gin
代码中引用
import "github.com/gin-gonic/gin"
(2)、启动一个gin服务
func RunGin() {
r := gin.Default()
r.GET("/health", handler.HealthHandler)
err := r.Run(configs.GetConfig().Gin.Listen)
if err != nil {
log.Fatalf("run gin server error: %s", err.Error())
}
log.Println("gin server success, http listening", addr)
}
//主函数
func Main(t *testing.T) {
RunGin()
}
3、主要特性介绍
(1)、高性能
Gin的高性能得益于其路由的实现基于httprouter这个高性能HTTP请求路由框架开发。
httprouter的快速匹配通过基数树这种数据结构实现,该数据结构在处理HTTP请求路由这种存在较多公共前缀、路由数量较少但每条记录较长的匹配场景下具有先天优势。
(2)、中间件
gin 官方推荐了很多第三方贡献的优秀中间件,同时也支持用户自定义中间件的开发,创建一个中间件非常简单,只需要实现一个 gin.HandlerFunc
方法即可。
自定义中间件示例
例如我们开发三个简单中间件分别用于统计全部接口、健康检测接口调用以及业务接口的鉴权。
// 自定义统计全部接口调用中间件
func CallCount() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("call gin server")
}
}
// 自定义统计group 分组 post 方法接口调用中间件
func CallPost() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("call post server")
}
}
// 自定义鉴权中间件
func Authorization(auth string) gin.HandlerFunc {
return func(c *gin.Context) {
var p common.AuthParam
var err error
if err = c.ShouldBindBodyWith(&p, binding.JSON); err != nil {
errMsg := fmt.Sprintf("bind auth param error: %s", err.Error())
c.JSON(http.StatusOK, common.Response{
Code: common.BadCode,
Message: errMsg,
Date: nil,
})
return
}
if p.Auth != auth {
errMsg := fmt.Sprintf("auth [%s] error", p.Auth)
c.JSON(http.StatusOK, common.Response{
Code: common.BadCode,
Message: errMsg,
Date: nil,
})
return
}
}
}
上述中间件的实现逻辑都非常简单,只是为了后续展示不同类型中间件的使用。
需要注意的一点是,当中间件和业务Handler中都需要读取gin.context
中的c.Request.Body
数据时,必须全部使用ShouldBindBodyWith
方法来实现,否则将发生 EOF 异常,具体原因参见上一篇文章 Gin多次读取HTTP请求体中body数据(ShouldBindBodyWith)
中间件的分类与调用
中间件按照使用位置及方法的不同可以分为三类:
- 全局中间件
- 局部中间件
- 单路由中间件
func RunGin() {
r := gin.Default()
r.Use(CallCount())
pprof.Register(r)
r.GET("/health", handler.HealthHandler)
group := r.Group("/api/v1", CallPost())
group.POST("/json/echo", Authorization("123456"),handler.JsonEchoHandler)
group.POST("/urlencoded/echo", handler.UrlencodedEchoHandler)
group.POST("/formdata/echo", handler.FormDataEchoHandler)er)
err := r.Run(configs.GetConfig().Gin.Listen)
if err != nil {
log.Fatalf("run gin server error: %s", err.Error())
}
log.Println("gin server success, http listening", addr)
}
三种不同中间件的具体区分方法如下:
- 使用
r.Use(CallCount())
注册的中间件就是全局中间件,它将在所有的路由中生效。 - 使用
group := r.Group("/api/v1", CallPost())
注册的就是局部中间件,它只在/api/v1
这一分组的路由中生效。 - 使用
group.POST("/json/echo", Authorization("123456"),handler.JsonEchoHandler)
注册的是单路由中间件,他只在///api/v1/json/echo
这一指定路由中生效。
(3)、无崩溃
Gin 内置了 panic recover 机制,它可以恢复gin服务执行过程中的任务 panic ,同时在返回结果中写入 HTTP状态码 500。因此当请求内部发生异常时,并不会影响整个系统的正常运行。
err = sendResuest error:get bad htp status code 500, resp = {{Status:500 Internal Server Error StatusCode:500 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Content-Length:[0] Date:[Thu, 23 Feb 2023 10:04:05 GMT]] Body:0xc000286020 ContentLength:0 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc00018c100 TLS:}
Gin 内置 panic recover 机制是通过 中间件实现的。如果使用gin.Default()
创建一个 gin 服务,会自动添加 Recover中间件。同时也可以通过 r.Use(gin.Recovery())
手动添加。
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
(4)、路由分组
为了对HTTP路由进行管理,Gin支持路由的分组,我们可以基于接口的版本、方法或者业务逻辑对多个具有进行分组,从而实现灵活的管理。例如上文中,我们结合中间件,可以对不同的路由分组,实现不同的鉴权、统计或者其他复杂逻辑的管理。
func RunGin() {
r := gin.Default()
r.Use(CallCount())
r.GET("/health", handler.HealthHandler)
group := r.Group("/api/v1", CallPost())
group.POST("/json/echo", Authorization("123456"),handler.JsonEchoHandler)
group.POST("/urlencoded/echo", handler.UrlencodedEchoHandler)
group.POST("/formdata/echo", handler.FormDataEchoHandler)
groupV2 := r.Group("/api/v2")
groupV2.POST("/json/echo",handler.JsonEchoHandler)
groupV2.POST("/urlencoded/echo", handler.UrlencodedEchoHandler)
groupV2.POST("/formdata/echo", handler.FormDataEchoHandler)
err := r.Run(configs.GetConfig().Gin.Listen)
if err != nil {
log.Fatalf("run gin server error: %s", err.Error())
}
log.Println("gin server success, http listening", addr)
}
(5)、内置渲染
Gin 内置了 String、Json、Yaml、Xml和Proto等渲染方式。如果对同一个接口提供上述五种渲染方式进行验证,具体效果如下:
func RunGin() {
r := gin.Default()
health := r.Group("/health")
health.GET("/string", handler.HealthHandler)
health.GET("/json", handler.HealthJsonHandler)
health.GET("/yaml", handler.HealthYamlHandler)
health.GET("/xml", handler.HealthXmlHandler)
err := r.Run(configs.GetConfig().Gin.Listen)
if err != nil {
log.Fatalf("run gin server error: %s", err.Error())
}
log.Println("gin server success, http listening", addr)
}
- string渲染
func HealthHandler(c *gin.Context) {
c.String(http.StatusOK, "ok")
}
结果样式:
map[message:success state:0]
- Json渲染
func HealthJsonHandler(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{"state":0,"message":"success"})
}
结果样式:
{
"message": "success",
"state": 0
}
- Yaml渲染
func HealthYamlHandler(c *gin.Context) {
c.YAML(http.StatusOK,gin.H{"state":0,"message":"success"})
}
结果样式:
message: success
state: 0
- Xml渲染
func HealthXmlHandler(c *gin.Context) {
c.XML(http.StatusOK,gin.H{"state":0,"message":"success"})
}
结果样式:
<map>
<state>0</state>
<message>success</message>
</map>
转载自:https://juejin.cn/post/7205111603464994874