Gin 使用
sync.Pool复用 Context 对象,减少内存分配,提升性能。理解这个机制有助于写出更高效的代码。
Go 的 sync.Pool 是一个临时对象池:
package main
import (
"sync"
)
func main() {
pool := &sync.Pool{
New: func() interface{} {
return &MyObject{}
},
}
obj := pool.Get().(*MyObject)
obj.Reset()
pool.Put(obj)
}
Gin 内部使用 sync.Pool 管理 Context:
type Engine struct {
RouterGroup
pool sync.Pool
trees methodTrees
Delims render.Delims
}
func New() *Engine {
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
}
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
func (engine *Engine) allocateContext() *Context {
return &Context{engine: engine}
}
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.Writer.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
每个请求都创建新 Context 会有大量内存分配:
func BenchmarkWithoutPool(b *testing.B) {
for i := 0; i < b.N; i++ {
c := &Context{}
_ = c
}
}
func BenchmarkWithPool(b *testing.B) {
pool := &sync.Pool{
New: func() interface{} {
return &Context{}
},
}
for i := 0; i < b.N; i++ {
c := pool.Get().(*Context)
pool.Put(c)
}
}
使用 Pool 可以显著减少 GC 压力。
每次使用前,Context 会被重置:
func (c *Context) reset() {
c.Writer = &responseWriter{}
c.Params = c.Params[0:0]
c.handlers = nil
c.index = -1
c.fullPath = ""
c.Keys = nil
c.Errors = c.Errors[0:0]
c.Accepted = nil
c.queryCache = nil
c.formCache = nil
c.(*bodyAllowed) = 0
}
func main() {
r := gin.Default()
r.GET("/bad", func(c *gin.Context) {
go func() {
time.Sleep(time.Second)
fmt.Println(c.Request.URL.Path)
}()
})
r.Run(":8080")
}
goroutine 执行时,Context 可能已被复用,导致数据错误。
r.GET("/good", func(c *gin.Context) {
cCp := c.Copy()
go func() {
time.Sleep(time.Second)
fmt.Println(cCp.Request.URL.Path)
}()
})
var globalContext *gin.Context
r.GET("/wrong", func(c *gin.Context) {
globalContext = c
})
如果需要自定义对象池:
type MyContext struct {
gin.Context
customField string
}
type MyEngine struct {
gin.Engine
customPool sync.Pool
}
func NewMyEngine() *MyEngine {
engine := &MyEngine{}
engine.customPool.New = func() interface{} {
return &MyContext{}
}
return engine
}
sync.Pool 管理频繁创建的对象var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func processRequest(c *gin.Context) {
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufferPool.Put(buf)
buf.WriteString("processing...")
c.String(200, buf.String())
}
import "runtime"
func monitorPool() {
for {
time.Sleep(time.Second)
var m runtime.MemStats
runtime.ReadMemStats(&m)
log.Printf("Alloc: %v MB, NumGC: %v", m.Alloc/1024/1024, m.NumGC)
}
}
Gin 使用 sync.Pool 复用 Context,减少内存分配和 GC 压力。理解这个机制,可以避免在异步操作中错误使用 Context。记住:不要持有 Context 引用,需要异步使用时调用 Copy() 方法。