前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Golang】跟着源码学技巧系列之对象池sync.Pool

【Golang】跟着源码学技巧系列之对象池sync.Pool

作者头像
DDGarfield
发布2022-06-23 19:19:37
4480
发布2022-06-23 19:19:37
举报
文章被收录于专栏:加菲的博客

在源码中学习一些技巧

1.从Run()开始

在go语言的gin框架中,通过.Run()启动web服务。我们查看源码:

代码语言:javascript
复制
//gin.go
func (engine *Engine) Run(addr ...string) (err error) {
 defer func() { debugPrintError(err) }()

 address := resolveAddress(addr)
 debugPrint("Listening and serving HTTP on %s\n", address)
 err = http.ListenAndServe(address, engine)
 return
}

其中ListenAndServenet/http库中指定的监听地址和处理器

代码语言:javascript
复制
//net/http/server.go
func ListenAndServe(addr string, handler Handler) error {
 server := &Server{Addr: addr, Handler: handler}
 return server.ListenAndServe()
}

可以看到gin源码中调用时的是engine *Engine作为Handler参数,继续查看一下Handler源码:

代码语言:javascript
复制
type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

果然没错Handler是一个接口,接口方法:ServeHTTP(ResponseWriter, *Request)

那么Engine必然实现了 **ServeHTTP**方法

通过VSCode导航,的确找到了Engine结构体实现了这个方法:

代码语言:javascript
复制
// ServeHTTP conforms to the http.Handler interface.
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)
}

2.sync.Pool登场

我们看上一节的第三行代码:

代码语言:javascript
复制
c := engine.pool.Get().(*Context)
代码语言:javascript
复制
type Engine struct {
 //omit code
    pool             sync.Pool
}

其中pool字段是sync.Pool类型,那到底什么是sync.Pool?

原来:这是go语言中典型的对象池的概念,为了减少GC,减少内存申请的频率,把可以重用的对象构造成一个对象池,engine.pool.Get() 就是从池子中捞出一个对象,强制转换为context指针。

  1. 通过对象池,减少每一次临时创建对象的内存申请和垃圾回收的消耗
  2. 频繁的对象申请与回收,就可以使用对象池优化代码
  3. 可以避免对象的引用或者其他的干扰,在不影响代码的实际功能,从池子里面去一个对象后,再做初始化

3.使用方法

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

3.1 声明

代码语言:javascript
复制
//gin.go
func New() *Engine {
 debugPrintWARNINGNew()
 engine := &Engine{
  RouterGroup: RouterGroup{
   Handlers: nil,
   basePath: "/",
   root:     true,
  },
  FuncMap:                template.FuncMap{},
  RedirectTrailingSlash:  true,
  RedirectFixedPath:      false,
  HandleMethodNotAllowed: false,
  ForwardedByClientIP:    true,
  AppEngine:              defaultAppEngine,
  UseRawPath:             false,
  RemoveExtraSlash:       false,
  UnescapePathValues:     true,
  MaxMultipartMemory:     defaultMultipartMemory,
  trees:                  make(methodTrees, 0, 9),
  delims:                 render.Delims{Left: "{{", Right: "}}"},
  secureJsonPrefix:       "while(1);",
 }
 engine.RouterGroup.engine = engine
    //实现New函数
 engine.pool.New = func() interface{} {
  return engine.allocateContext()
 }
 return engine
}
  • 只需要实现 New 函数即可。对象池中没有对象时,将会调用 New 函数创建。

3.2 Get()

获取

  • Get() 用于从对象池中获取对象,因为返回值是 interface{},因此需要类型转换,上面的代码已经有展示。
代码语言:javascript
复制
c := engine.pool.Get().(*Context)

3.3 Put()

放回

  • Put() 则是在对象使用完毕后,返回对象池。

处理完http请求后,又把context放回到对象池中:

代码语言:javascript
复制
engine.handleHTTPRequest(c)
engine.pool.Put(c)

3.注意事项

sync.Pool是可伸缩的,并发安全的。其大小仅受限于内存的大小,可以被看作是一个存放可重用对象的值的容器。它的设计的目的是存放已经分配的但是暂时不用的对象,在需要用到的时候直接从pool中取。任何存放区其中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩。

由于上面加粗字体的原因,所以对象池比较适合用来存储一些临时切状态无关的数据,因为存入对象池的值有可能会在垃圾回收时被删除掉。http请求的context上下文就是这样的类型。

4.再补充一个技巧

gin源码中定义Engine结构体的下面有一句:

代码语言:javascript
复制
var _ IRouter = &Engine{}

一个不起眼的匿名变量:匿名变量不占用内存空间,不会分配内存,那它到底有什么用?不可能是作者写忘了,这么著名的开源库。

原来go语言经常会遇到一个场景,在编写完一个结构体之后,这个结构体会实现很多接口,问题是,代码繁杂以后,谁还记得哪个接口到底实现没有,怎么办?

  • 人工查验,嗯,是个方法,
  • Ctrl+F比对,找方法接收者,嗯,也是一个方法

但是,更棒的方法,是靠编译器:

定义一个匿名变量 var _ 接口类型=结构体类型指针,这个主要是确保结构体确实实现了接口,目的就是把问题暴露在编译阶段,很多第三方库和标准库都是这样做的,值得学习。

参考链接

https://www.cnblogs.com/sunsky303/p/9706210.html

https://geektutu.com/post/hpg-sync-pool.html

------------------- End -------------------

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 加菲的博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.从Run()开始
  • 2.sync.Pool登场
  • 3.使用方法
    • 3.1 声明
      • 3.2 Get()
        • 3.3 Put()
        • 3.注意事项
        • 4.再补充一个技巧
        • 参考链接
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档