Mygin上下文之sync.Pool复用

本篇是mygin的第七篇,参照gin框架,感兴趣的可以从 Mygin第一篇 开始看,Mygin从零开始完全手写,在实现的同时,带你一窥gin框架的核心原理实现。

目的

  • sync.Pool 的作用介绍
  • mygin中使用sync.Pool

sync.Pool 的作用

先看看官方文档怎样说的吧,我截取了官方文档的第一句。

Golang 复制代码
// A Pool is a set of temporary objects that may be individually saved and retrieved.
.....
  • 简单翻译一下的意思是:池是一组可以单独保存和检索的临时对象。既然可以单独保存和检索的临时对象,对于大量重复地创建许多对象,造成 GC 的工作量巨大。而mygin的模式是 责任链模式 ,因此满足使用 sync.Pool。
  • 一个 Pool 可以安全地由多个 goroutine 同时使用。池的目的是缓存已分配但未使用的项目以供以后重用,从而减轻垃圾回收器的压力。
  • sync.Pool 是可伸缩的,同时也是并发安全的,其大小仅受限于内存的大小。sync.Pool 用于存储那些被分配了但是没有被使用,而未来可能会使用的值。这样就可以不用再次经过内存分配,可直接复用已有对象,减轻 GC 的压力,从而提升系统的性能。
    以上都是源于官方文档翻译的,文档中还提到fmt包中,打印也使用了sync.Pool,感兴趣的可以点进源码查看。

sync.Pool 使用

sync.Pool 的使用方式非常简单:

只需要实现New 函数即可。对象池中没有对象时,将会调用New函数创建,我使用了mygin中的context

创建

Golang 复制代码
var contextPool = sync.Pool{
	New: func() interface{} {
		return new(Context)
	},
}

使用和归还

Golang 复制代码
c := contextPool.Get().(*Context)
json.Marshal(c)
contextPool.Put(c)

测试

Golang 复制代码
func BenchmarkUnmarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c := &Context{}

		json.Marshal(c)
	}
}

func BenchmarkUnmarshalWithPool(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c := contextPool.Get().(*Context)
		json.Marshal(c)
		contextPool.Put(c)
	}
}

测试结果:

shell 复制代码
go test -bench . -benchmem
goos: linux
goarch: amd64
pkg: github.com/scott-pb/mygin
cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
BenchmarkUnmarshal-8             5888780               208.1 ns/op           144 B/op          2 allocs/op
BenchmarkUnmarshalWithPool-8     7261801               165.0 ns/op            48 B/op          1 allocs/op
PASS
ok      github.com/scott-pb/mygin       2.808s

在这个例子中,可以看出,使用了 sync.Pool 后,内存占用仅为未使用的 48/144= 1/3,对 GC 的影响就很大了。执行速度也快了,当然不同的设备测试结果也会不同。

测试源码

我把测试源码放在了mygin中 mygin/context_test.go

mygin使用sync.Pool

修改mygin/engine.go

修改engine.go 中实例化conetxt的部分具体在ServeHTTP 方法中

修改前

Golang 复制代码
//实例化一个下上文
c := &Context{
	Request:  r,
	Writer:   w,
	Params:   params,
	handlers: handlers,
	index:    -1,
}

修改后

复制代码
//从pool中取
c := e.pool.Get().(*Context)
c.Request = r
c.Writer = w
c.Params = params
c.handlers = handlers
c.index = -1

// 执行处理函数链
c.Next()

//归还到pool中
e.pool.Put(c)

mygin测试

main方法代码如下

Golang 复制代码
package main

import (
	"fmt"
	"github.com/scott-pb/mygin"
	"net/http"
)

func main() {

	r := mygin.Default()
	group := r.Group("/api")
	group.GET("/test", func(c *mygin.Context) {
		c.String(http.StatusOK, "success!\n")
	})

	err := r.Run(":8088")
	if err != nil {
		fmt.Println(err)
	}
}

curl测试

shell 复制代码
curl -i http://localhost:8088/api/test
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Thu, 01 Feb 2024 05:08:52 GMT
Content-Length: 9

success!

这样mygin的context上下文就加入了Pool池,对于高并发情况下的GC压力会减轻不少。我设计的上下文中内容很少,随着功能的增多,效果会更加明显。

相关推荐
背帆10 分钟前
数据转储(go)
数据库·golang
Wenhao.1 小时前
Go-web开发之帖子功能
开发语言·前端·golang
恋喵大鲤鱼1 小时前
Golang 身份证号码校验
开发语言·后端·golang
Saggitarxm2 小时前
Golang - 实现文件管理服务器
服务器·开发语言·golang
liupenglove4 小时前
一个读写excel的简单程序(golang)
数据仓库·后端·golang·excel
小黑随笔12 小时前
【Golang玩转本地大模型实战(二):基于Golang + Web实现AI对话页面】
前端·人工智能·golang
城里有一颗星星16 小时前
基于go的简单管理系统(增删改查)
开发语言·后端·golang
言之。1 天前
Go语言中的错误处理
开发语言·后端·golang
席万里1 天前
vscode详细配置Go语言相关插件
ide·vscode·golang
robch2 天前
golang接口和具体实现之间的类型转换
开发语言·后端·golang