@

概述

定义

Gin 官网地址 https://gin-gonic.com/ 源码release最新版本v1.9.1

Gin 官网文档地址 https://gin-gonic.com/docs/

Gin 源码地址 https://github.com/gin-gonic/gin

Gin是目前使用最广泛、最快的全功能web框架之一,采用Go语言(Golang)编写HTTP 服务,与它类似如martini-like API ,但Gin性能更好,基于httprouter其速度快了40倍。

Gin是一种用于构建Web应用程序的Go语言框架,具有高性能、易于使用、轻量级和灵活的特点。 Gin提供了许多功能,例如路由、中间件、错误处理等。同时,Gin还可以与许多其他Go语言库和框架无缝集成。 Gin的设计目的是提供一种快速、可靠和高效的方式来构建Web应用程序,以满足现代Web应用程序的需求;它的文档和社区支持也非常好,因此它成为了Go语言中最受欢迎的Web框架之一。

特点

  • 快:基于Radix tree的路由,内存占用小;没有反射,可预测的API性能。
  • 中间件支持:传入的HTTP请求可以由一系列中间件和最终操作来处理。例如:Logger, Authorization, GZIP,最后在DB中发布消息。
  • 无故障:Gin可以捕获HTTP请求期间发生的panic并恢复它保证服务器的可用性;例如可以向哨兵报告这种panic。
  • JSON验证:Gin可以解析和验证请求的JSON,例如检查是否存在所需值。
  • 路由分组:更好地组织路由,如需要授权与不需要授权、不同的API版本;此外路由组可以无限嵌套且不会降低性能。
  • 错误管理:Gin提供了一种方便的方法来收集HTTP请求期间发生的所有错误。最终,中间件可以将它们写入日志文件、数据库并通过网络发送。
  • 呈现内置:Gin为JSON、XML和HTML渲染提供了一个易于使用的API。
  • 可扩展的:非常简单通过自定义创建新的中间件实现扩展功能。

概览导图

使用

快速入门

前置环境:需要go 1.13及以上版本

# 下载并安装
go get -u github.com/gin-gonic/gin

创建quick_start.go文件,导入gin库

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default()
// 配置路由映射
r.GET("/hello", func(c *gin.Context) {
c.String(http.StatusOK, "hello,welcome to go world!")
})
// 监听端口,默认为0.0.0.0:8080
r.Run()
}

启动运行,访问http://localhost:8080/hello可以看到成功返回信息

go run quick_start.go

HTTP 方法使用

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func demoGet(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo get",
})
} func demoPost(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo post",
})
} func demoPut(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo put",
})
} func demoDelete(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo delete",
})
} func demoPatch(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo patch",
})
} func demoHead(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo head",
})
} func demoOptions(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo options",
})
} func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default() // 配置路由映射
r.GET("/DemoGet", demoGet)
r.POST("/DemoPost", demoPost)
r.PUT("/DemoPut", demoPut)
r.DELETE("/DemoDelete", demoDelete)
r.PATCH("/DemoPatch", demoPatch)
r.HEAD("/DemoHead", demoHead)
r.OPTIONS("/DemoOptions", demoOptions) r.Run() // 监听端口,默认为0.0.0.0:8080
}

运行程序,通过HTTP请求工具如PostMan输入请求地址http://localhost:8080/DemoPost,返回预期结果

参数获取

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func getVal(c *gin.Context) {
name := c.Query("name")
addr := c.DefaultQuery("addr", "home")
c.JSON(http.StatusOK, gin.H{
"name": name,
"addr": addr,
})
}
func postVal(c *gin.Context) {
name := c.PostForm("name")
addr := c.DefaultPostForm("addr", "home")
c.JSON(http.StatusOK, gin.H{
"name": name,
"addr": addr,
})
}
func restVal(c *gin.Context) {
name := c.Param("name")
addr := c.Param("addr")
c.JSON(http.StatusOK, gin.H{
"name": name,
"addr": addr,
})
} func postMapVal(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
c.JSON(http.StatusOK, gin.H{
"ids": ids,
"names": names,
})
} func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default() // 配置路由映射
r.GET("/GetVal", getVal)
r.POST("/PostVal", postVal)
r.POST("/PostMapVal", postMapVal)
r.POST("/RestVal/:name/:addr", restVal) r.Run() // 监听端口,默认为0.0.0.0:8080
}

运行程序,通过HTTP请求工具如PostMan输入请求地址http://localhost:8080/GetVal?name=itxiaoshen,返回预期结果

用Post提交方式,输入请求地址http://localhost:8080/PostVal并选择Body的中form-data或者x-www-form-urlencoded填写参数,返回预期结果

输入请求地址http://localhost:8080/PostMapVal?ids[a]=hello&ids[b]=world,在Body体填写相应的数组参数值,通过数组返回并输出预期结果

输入请求地址http://localhost:8080/RestVal/lixuefeng/qinghua,在url路径参数中输入相应的参数值,返回预期结果

参数绑定

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) type Person struct {
Name string `form:"name"`
Address string `form:"addr"`
Age int
} type PersonUri struct {
Name string `uri:"name" binding:"required"`
Address string `uri:"addr" binding:"required"`
Age int `uri:"age" binding:"required"`
} func bindVal(c *gin.Context) {
var person Person
if c.ShouldBind(&person) == nil {
c.JSON(http.StatusOK, gin.H{
"name": person.Name,
"addr": person.Address,
"age": person.Age,
})
}
} func personMethod(c *gin.Context) {
var person PersonUri
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"msg": err})
return
}
c.JSON(http.StatusOK, gin.H{
"name": person.Name,
"addr": person.Address,
"age": person.Age,
})
} func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default() // 配置路由映射
r.GET("/BindVal", bindVal)
r.GET("/person/:name/:addr/:age", personMethod) r.Run() // 监听端口,默认为0.0.0.0:8080
}

运行程序,输入请求地址http://localhost:8080/BindVal?name=wangchuanjun&addr=zhengjiang&age=20,由于传入age字段与Person的Age字段名没匹配上,因此Age字段获取不到值采用的默认值0输出

通过uri路径参数输入请求地址http://localhost:8080/person/yangju/chengsan/26,返回预期结果

自定义日志输出

package main

import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
) func main() {
r := gin.New()
// LoggerWithFormatter中间件将把日志写入gin.DefaultWriter,默认为gin.DefaultWriter = os.Stdout
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// 自定义输出日志
return fmt.Sprintf("custom log format:%s -------- [%s] \"%s %s %d %s \"%s\" %s\"%s\n",
param.ClientIP,
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
param.TimeStamp.Format(time.RFC1123),
)
}))
r.Use(gin.Recovery())
r.GET("/hello", func(c *gin.Context) {
c.String(http.StatusOK, "hello,welcome to go world!")
})
r.Run(":8080")
}

启动运行,访问http://localhost:8080/hello,查看运行的控制台日志可以看到自定义日志输出

自定义中间件

package main

import (
"github.com/gin-gonic/gin"
"log"
"net/http"
"time"
) // 返回gin.HandlerFunc函数
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now() // 设置 userId 变量
c.Set("userId", "10001") // 请求前 c.Next() // 请求后
latency := time.Since(t)
log.Print(latency) // access the status we are sending
status := c.Writer.Status()
log.Println(status)
}
} func main() {
r := gin.New()
// 使用自定义中间件
r.Use(Logger()) r.GET("/hello", func(c *gin.Context) {
userId := c.MustGet("userId").(string)
c.String(http.StatusOK, "userId="+userId)
}) // 监听 0.0.0.0:8080
r.Run(":8080")
}

启动运行,访问http://localhost:8080/hello,查看运行的控制台日志可以看到输出响应的日志,也响应userId结果

路由组

路由组可以多级嵌套,实现细粒度控制

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func demoGet(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo get",
})
} func demoPost(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "demo post",
})
} func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default() v1 := r.Group("/v1")
{
v1.GET("/DemoGet", demoGet)
v1.POST("/DemoPost", demoPost)
}
v2 := r.Group("/v2")
{
v2.GET("/DemoGet", demoGet)
v2.POST("/DemoPost", demoPost)
} r.Run() // 监听端口,默认为0.0.0.0:8080
}

运行程序,访问http://localhost:8080/v2/DemoGet,返回预期结果

HTML渲染

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
// 可以通过下面LoadHTMLFiles加载指定的文件
//router.LoadHTMLFiles("templates/index.html", "templates/index1.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"content": "my website begin",
})
})
router.Run(":8080")
}

运行程序,访问http://localhost:8080/index,返回预期结果

设置和获取Cookie

package main

import (
"fmt"
"github.com/gin-gonic/gin"
) func main() {
router := gin.Default() router.GET("/cookie", func(c *gin.Context) { cookie, err := c.Cookie("gin_cookie") if err != nil {
cookie = "NotSet"
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
} fmt.Printf("Cookie value: %s \n", cookie)
}) router.Run()
}

运行程序,访问http://localhost:8080/index,可以查看cookie信息

XML、YAML、ProtoBuf渲染

package main

import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/testdata/protoexample"
"net/http"
) func main() {
r := gin.Default() r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
}) r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
}) r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
c.ProtoBuf(http.StatusOK, data)
}) r.Run(":8080")
}

运行程序,访问http://localhost:8080/someYAML,可以查看渲染内容

使用BasicAuth中间件

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) // 模拟一些私有数据
var secrets = gin.H{
"jasper": gin.H{"email": "jasper@163.com", "phone": "18821212121"},
"lili": gin.H{"email": "lili@163.com", "phone": "18821212122"},
"sam": gin.H{"email": "sam@163.com", "phone": "18821212123"},
} func main() {
r := gin.Default() // 使用gin.BasicAuth()中间件进行分组,实际中这里应该是从数据库中获取,gin.Accounts是map[string]string
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
"jasper": "123456",
"austin": "123456",
"lili": "123456",
"sam": "123456",
})) authorized.GET("/secrets", func(c *gin.Context) {
// 获取user,它是由BasicAuth中间件设置的
user := c.MustGet(gin.AuthUserKey).(string)
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
}
}) // 监听 0.0.0.0:8080
r.Run(":8080")
}

运行程序,浏览器上访问http://localhost:8080/admin/secrets,在弹出认证窗口上输入用户名密码如jasper/123456

点击登录按钮后则返回预期响应信息。

静态文件和BootStrap

# getbootstrap官网地址:https://getbootstrap.com
# 下载最新版本5.3.0bootstrap
wget https://github.com/twbs/bootstrap/releases/download/v5.3.0/bootstrap-5.3.0-dist.zip

解压目录下css和js目录拷贝到工程目录下,这里将css和js目录放在根目录下的assets目录下,然后创建main.go

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func index(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
} func main() {
// 创建一个带有默认中间件(logger and recovery (crash-free) 中间件)的gin.Engine
r := gin.Default() // 配置静态资源
r.Static("/assets", "assets")
r.Static("/favicon.ico", "assets/favicon.ico") // 配置路由映射
r.GET("/index", index) //加载html模版文件
r.LoadHTMLGlob("templates/*") r.Run() // 监听端口,默认为0.0.0.0:8080
}

这里从getbootstrap官网上找一个组件放到html文件中

在templates目录下创建index.html模版文件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../assets/css/bootstrap.min.css">
<script src="../assets/js/bootstrap.min.js"></script>
<title>静态资源及BootStrap示例</title>
</head>
<body>
<h1>
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-light">Light</button>
<button type="button" class="btn btn-dark">Dark</button>
<button type="button" class="btn btn-link">Link</button>
</h1>
</body>
</html>

上面在html配置link和script时前面加了../,这样才可以在IDE环境中定位到,总体目录结构如下

运行程序,访问http://localhost:8080/index ,已经成功加载到静态资源

使用Session

关于Session的可以使用第三方库的方式,在https://pkg.go.dev/上搜索go session,选择第一个github.com/gin-contrib/sessions,其支持多后端会话管理的Gin中间件包括cookie-based、Redis、memcached、MongoDB、GORM、memstore、PostgreSQL。

# 下载并安装
go get github.com/gin-contrib/sessions
# 在代码中导入
import "github.com/gin-contrib/sessions"
package main

import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
) func main() {
r := gin.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store)) r.GET("/session", func(c *gin.Context) {
session := sessions.Default(c) if session.Get("mykey") != "myvalue" {
session.Set("mykey", "myvalue")
session.Save()
} c.JSON(200, gin.H{"mykey": session.Get("mykey")})
})
r.Run(":8080")
}

运行程序,访问http://localhost:8080/session ,成功返回预期结果

上面示例使用的是cookie-based后端存储方式,下面则演示redis作为后端存储示例

package main

import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/redis"
"github.com/gin-gonic/gin"
) func main() {
r := gin.Default()
store, _ := redis.NewStore(10, "tcp", "localhost:6379", "123456", []byte("secret"))
r.Use(sessions.Sessions("myredissession", store)) r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("mycount")
if v == nil {
count = 0
} else {
count = v.(int)
count++
}
session.Set("mycount", count)
session.Save()
c.JSON(200, gin.H{"mycount": count})
})
r.Run(":8080")
}

项目目录下命令行执行go mod tidy,然后多次访问http://localhost:8080/incr,可以看到mycount一直在自增

写入日志文件

package main

import (
"github.com/gin-gonic/gin"
"io"
"os"
) func main() {
// 禁用控制台颜色,在将日志写入文件时不需要控制台颜色。
gin.DisableConsoleColor() // 记录到文件
f, _ := os.Create("my_app.log")
gin.DefaultWriter = io.MultiWriter(f) // 如果需要同时将日志写入文件和控制台,请使用以下代码
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout) router := gin.Default()
router.GET("/hello", func(c *gin.Context) {
c.String(200, "welcome to go world!")
}) router.Run(":8080")
}

启动程序,多次访问http://localhost:8080/hello,可以看到在当前目录下生成了my_app.log文件,用户可以结合日志需要比如使用logrus和file-rotatelogs库实现。

原理

核心执行流程

Gin整体流程还是比较简单的,从上面使用代码示例也可以看到基础执行流程大致如下:

  • 创建并初始化Engine对象
  • 注册middleware(中间件)
  • 注册路由及处理函数
  • 服务端口监听

主要设计核心方法流程

  • gin.Default():gin的初始化方法,目的是为了创建整个引擎,并初始化相关参数如RouterGroup、pool等。

使用gin框架开发时一般情况下使用默认的engine即可,因为相对于直接使用gin.New()创建Engine对象,它只是多注册了两个中间件。

func Default() *Engine {
debugPrintWARNINGDefault()
// 创建引擎
engine := New()
// 默认使用Logger和Recovery
engine.Use(Logger(), Recovery())
return engine
}

  • router.Use():使用中间件的方法,将请求过程中需要调用的中间件放入到HandlersChain,这个是一个数组,比如在请求前后需要加入通用方法如鉴权、自定义日志格式等。
// Use将全局中间件附加到路由器上。也就是说,通过Use()附加的中间件将是包含在每个请求(甚至404、405、静态文件)的处理程序链中,例如记录器或错误管理中间件的正确位置。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
} // 将中间件添加到路由组中
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}

  • router.GET():构建url和具体处理请求的handle的关系了,其实目标很明确,就是要将这组关系存入到最终的trees中去。
// 链接group.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)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
} func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] == '/', "path must begin with '/'")
assert1(method != "", "HTTP method can not be empty")
assert1(len(handlers) > 0, "there must be at least one handler") debugPrintRoute(method, path, handlers) root := engine.trees.get(method)
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers) // Update maxParams
if paramsCount := countParams(path); paramsCount > engine.maxParams {
engine.maxParams = paramsCount
} if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
engine.maxSections = sectionsCount
}
}

  • router.Run():内部流程主要是调用golang中net/http包下的方法监听服务端口。
// Run将路由器附加到http上,服务器并开始监听和服务HTTP请求;也即是http.ListenAndServe快捷方式,对于除非发生错误,否则此方法将无限期地阻塞调用例程
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)
err = http.ListenAndServe(address, engine.Handler())
return
}

  • ServeHTTP:接收请求请求的处理在gin.go中的ServeHTTP,其处理http.Handler接口。其根据请求的url和method找到对应的handle去处理,通过数查找去。同时利用context进行参数传递,用c.Next进行递归遍历中间件的调用,handle是一个链式过程。
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)
}

核心数据结构

在New()创建*Engine的方法中可以看到初始化重要信息,

其核心组成的重要结构如下

Engine是一个总的引擎,保存了各个组件的信息 ,其他组件信息如下:

  • RouterGroup是一个路由组,路由管理相关,保存了路由信息 。路由组的目的是为了实现配置的复用,相关的请求使用一组单独的middleware;gin.Engine对象本身就是一个路由组。
type RouterGroup struct {
// 路由组处理函数链,其下路由的函数链将结合路由组和自身的函数组成最终的函数链
Handlers HandlersChain
// 路由组的基地址,一般是其下路由的公共地址
basePath string
// 路由组所属的Engine,这里构成了双向引用
engine *Engine
// 该路由组是否位于根节点,基于RouterGroup.Group创建路由组时此属性为false
root bool
}

在RouterGroup数据结构有一个非常重要的成员字段HandlersChain(处理器链 ),用于收集该路由组下注册的middleware函数。在运行时,会按顺序执行HandlersChain中的注册的函数。

// HandlersChain 定义为一个HandlerFunc切片.
type HandlersChain []HandlerFunc
// HandlerFunc 定义gin中间件使用的处理程序作为返回值
type HandlerFunc func(*Context)
  • 路由树数组trees:trees是一棵树,保存了url与handle的映射关系 ,粗暴一点可以简单理解为key就是url字符串,value对应的[]HandleFunc;标准库本身的路由是不区分请求方法的,也就是说注册一个路由后,GET、POST都能匹配到该路由,而还需要在同一个路由在不同的请求方法下,由不同的逻辑进行处理。其实就是通过路由树实现的,gin的针对每个请求方法都有一棵路由树。Gin利用基于Radix Tree基数树思想通过优秀的数据结构和算法设计达到高性能目标。其核心实现是在gin的tree.go源码文件中。

    • Radix Tree是一种基于 Trie(字典树)的数据结构,旨在解决字符串搜索和匹配的问题。它最早由 Fredkin 在 1960 年提出,并在之后被广泛应用于各种应用领域。其最大的特点就是在 Trie 的基础上,加入了路径压缩的逻辑,通过合并前缀的方式大大的减少了 Trie 中的节点冗余问题,不仅提高了查询效率,还减少了存储空间的使用。
  • context对象池:engine中的pool用于复用Context,gin.Context是gin框架暴露给开发的另一个核心对象,可以通过该对象获取请求信息,业务处理的结果也是通过该对象写回客户端的。为了实现context对象的复用,gin基于sync.Pool实现了对象池。由于请求多会产生很多数量的context,利用pool来重复利用对象,从而减少内存的分配也提高了效率。

  • Context:包含了Request,Writer等信息,用于request中传递值。

  • 本人博客网站IT小神 www.itxiaoshen.com

云原生时代Go最受欢迎Web开源框架Gin原理与实战的更多相关文章

  1. 云原生时代之Kubernetes容器编排初步探索及部署、使用实战-v1.22

    概述 **本人博客网站 **IT小神 www.itxiaoshen.com Kubernetes官网地址 https://kubernetes.io Kubernetes GitHub源码地址 htt ...

  2. 云原生时代,Java的危与机(周志明)

    说明 本篇文章是转载自周志明老师的文章,链接地址:https://www.infoq.cn/article/RQfWw2R2ZpYQiOlc1WBE 今天,25 岁的 Java 仍然是最具有统治力的编 ...

  3. 云原生时代的Java

    原文链接(作者:周志明):https://time.geekbang.org/column/article/321185 公开课链接:https://time.geekbang.org/opencou ...

  4. 云原生之旅 - 9)云原生时代网关的后起之秀Envoy Proxy 和基于Envoy 的 Emissary Ingress

    前言 前一篇文章讲述了基于Nginx代理的Kuberenetes Ingress Nginx[云原生时代的网关 Ingress Nginx]这次给大家介绍下基于Envoy的 Emissary Ingr ...

  5. 阿里云弹性容器实例产品 ECI ——云原生时代的基础设施

    阿里云弹性容器实例产品 ECI ——云原生时代的基础设施 1. 什么是 ECI 弹性容器实例 ECI (Elastic Container Instance) 是阿里云在云原生时代为用户提供的基础计算 ...

  6. 进击的 Java ,云原生时代的蜕变

    作者| 易立 阿里云资深技术专家 导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的.然而,本文的作者却认为云原生时代,Java 依然可以胜任&qu ...

  7. 进击的.NET 在云原生时代的蜕变

    你一定看过这篇文章 <进击的 Java ,云原生时代的蜕变>,  本篇文章的灵感来自于这篇文章.明天就将正式发布.NET Core 3.0, 所以写下这篇文章让大家全面认识.NET Cor ...

  8. 开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门

    点击下载<Knative 云原生应用开发指南> 自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注.Knative 在 Kubernetes 之上提供了一套完整的应 ...

  9. [转帖]从 SOA 到微服务,企业分布式应用架构在云原生时代如何重塑?

    从 SOA 到微服务,企业分布式应用架构在云原生时代如何重塑? 2019-10-08 10:26:28 阿里云云栖社区 阅读数 54   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权 ...

  10. .NET 在云原生时代的蜕变,让我在云时代脱颖而出

    .NET 生态系统是一个不断变化的生态圈,我相信它正在朝着一个伟大的方向发展.有了开源和跨平台这两个关键优先事项,我们就可以放心了.云原生对应用运行时的不同需求,说明一个.NET Core 在云原生时 ...

随机推荐

  1. flex布局相关属性记录

    <template> <div class="about"> <h3>flex相关的属性</h3> <div class=&q ...

  2. PHP 图片的合并,微信小程序码合并,文字合并

    //业务需求:我们需要一个微信小程序码,但是是需要提供给别人扫码的但是只有一个纯粹的小程序码是不好看的,所以需要推广的海报图片.再结合文字 最终效果 准备工作  1.需要海报的底图  2.小程序码的图 ...

  3. 移动端测试辅助工具 - adb

    1. 概念: adb(android debug bridge)是android提供的基于CS架构的命令行调试工具,使PC与安卓设备之间实现通信 2. 基础原理: 交互图: 主要由三部分组成: adb ...

  4. linux服务器qps查询,查看当前linux服务器的QPS

    https://blog.csdn.net/weixin_42119281/article/details/116595205 QPS:每秒查询率(QPS,Queries-per-second)是对一 ...

  5. win10计划任务程序库实现定时任务的自动执行程序及问题解决。

    win10计划任务程序库可以实现按照规则频率执行脚本的功能.现在将设置方法记录如下: 创建任务步骤 1.右键点击我的电脑,选择管理,依次点击:系统工具->任务计划程序->任务计划程序库. ...

  6. python线程之event事件

    from threading import Thread, Event import time event = Event() def light(): print('红灯亮着,所有车都要等待') t ...

  7. Vulnhub Bravery靶机 Walkthrough

    Bravery Recon 使用netdiscover对本地网络进行arp扫描. ┌──(kali㉿kali)-[~] └─$ sudo netdiscover -r 192.168.80.0/24 ...

  8. 浅谈对属性描述符__get__、__set__、__delete__的理解

    1.属性描述符的基础介绍 1.1 何为属性描述符? 属性描述符是一种Python语言中的特殊对象,用于定义和控制类属性的行为.属性描述符可以通过定义__get__.__set__.__delete__ ...

  9. Spring Boot 整合 xxl-job

    官方文档:https://www.xuxueli.com/xxl-job/ XXL-JOB 是一个分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并接入多家公司线 ...

  10. 虚拟内存与malloc/new原理详解

    malloc malloc()函数并不是系统调用,而是 C 库里的函数,用于动态分配内存.malloc() 分配的是虚拟内存,而不是物理内存.如果分配后的虚拟内存没有被访问的话,是不会将虚拟内存映射到 ...