gpool

对象复用池。将对象进行缓存复用,支持过期时间创建方法销毁方法定义。

使用场景

任何需要对象(struct)复用的场景。

使用方式

  1. import "gitee.com/johng/gf/g/container/gpool"

方法列表godoc.org/github.com/johng-cn/gf/g/container/gpool

  1. type Pool
  2. func New(expire int, newFunc ...func() (interface{}, error)) *Pool
  3. func (p *Pool) Close()
  4. func (p *Pool) Get() (interface{}, error)
  5. func (p *Pool) Put(value interface{})
  6. func (p *Pool) SetExpireFunc(expireFunc func(interface{}))
  7. func (p *Pool) Size() int

需要注意两点:

  1. New方法的过期时间单位为毫秒
  2. 对象创建方法(newFunc ...func() (interface{}, error))返回值包含一个error返回,当对象创建失败时可由该返回值反馈原因;

gpool与sync.Pool

gpoolsync.Pool的作用都是对象复用,但是sync.Pool的对象生命周期不支持开发者定义,因此也不确定,并且sync.Pool不支持对象创建方法及销毁方法。因此,gpool的功能更强大,并且更接近实战一些。

使用示例1,基本使用

  1. package main
  2. import (
  3. "gitee.com/johng/gf/g/container/gpool"
  4. "fmt"
  5. "time"
  6. )
  7. func main () {
  8. // 创建一个对象池,过期时间为1000毫秒
  9. p := gpool.New(1000)
  10. // 从池中取一个对象,返回nil及错误信息
  11. fmt.Println(p.Get())
  12. // 丢一个对象到池中
  13. p.Put(1)
  14. // 重新从池中取一个对象,返回1
  15. fmt.Println(p.Get())
  16. // 等待1秒后重试,发现对象已过期,返回nil及错误信息
  17. time.Sleep(time.Second)
  18. fmt.Println(p.Get())
  19. }

使用示例2,文件指针池

这个示例稍微复杂一些,但是却将gpool对象池的功能用得很完美。

该示例是一个gf框架的gfilepool文件指针池的实现源码。文件指针池和数据库连接池比较类似,是基于IO复用的一种设计。

一般的文件操作过程是这样的:当文件被打开之后,会创建一个文件指针,在文件操作结束后,文件指针就会被立即关闭掉。这样的话,在每一次操作或者请求中,对文件的操作都会反复创建/关闭指针,这样产生的问题:一个是操作效率比较低,另一个是系统的最大文件打开数是有限的,在文件操作频繁/访问量大的情况下,会影响整体系统的执行效率(文件锁,资源竞争),甚至引发一些严重的问题。文件指针池对于同一个文件在异步高并发下的读取性能提升非常大(因为读取不需要锁,每一个协程一个文件指针),但是对于写入性能的提升不大,因为文件写入往往都必须加锁以保证数据写入的顺序性。

  1. package gfilepool
  2. import (
  3. "os"
  4. "sync"
  5. "gitee.com/johng/gf/g/container/gmap"
  6. "gitee.com/johng/gf/g/container/gpool"
  7. "fmt"
  8. )
  9. // 文件指针池
  10. type Pool struct {
  11. pool *gpool.Pool // 底层对象池
  12. }
  13. // 文件指针池指针
  14. type File struct {
  15. *os.File // 底层文件指针
  16. mu sync.RWMutex // 互斥锁
  17. pool *Pool // 所属池
  18. }
  19. // 全局指针池,expire < 0表示不过期,expire = 0表示使用完立即回收,expire > 0表示超时回收
  20. var pools = gmap.NewStringInterfaceMap()
  21. // 获得文件对象,并自动创建指针池
  22. func OpenWithPool(path string, flag int, perm os.FileMode, expire int) (*File, error) {
  23. key := fmt.Sprintf("%s&%d&%d&%d", path, flag, expire, perm)
  24. result := pools.Get(key)
  25. if result != nil {
  26. return result.(*Pool).File()
  27. }
  28. pool := New(path, flag, perm, expire)
  29. pools.Set(key, pool)
  30. return pool.File()
  31. }
  32. // 创建一个文件指针池,expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收
  33. func New(path string, flag int, perm os.FileMode, expire int) *Pool {
  34. p := &Pool {}
  35. p.pool = gpool.New(expire, func() (interface{}, error) {
  36. file, err := os.OpenFile(path, flag, perm)
  37. if err != nil {
  38. return nil, err
  39. }
  40. return &File{
  41. File : file,
  42. pool : p,
  43. }, nil
  44. })
  45. p.pool.SetExpireFunc(func(i interface{}) {
  46. i.(*File).File.Close()
  47. })
  48. return p
  49. }
  50. // 获得一个文件打开指针
  51. func (p *Pool) File() (*File, error) {
  52. if v, err := p.pool.Get(); err != nil {
  53. return nil, err
  54. } else {
  55. return v.(*File), nil
  56. }
  57. }
  58. // 关闭指针池(返回error是标准库io.ReadWriteCloser接口实现)
  59. func (p *Pool) Close() error {
  60. p.pool.Close()
  61. return nil
  62. }
  63. // 获得底层文件指针(返回error是标准库io.ReadWriteCloser接口实现)
  64. func (f *File) Close() error {
  65. f.pool.pool.Put(f)
  66. return nil
  67. }