├── .travis.yml ├── .gitignore ├── LICENSE ├── README.md ├── pool_test.go └── pool.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1.x 5 | - 1.2.x 6 | - 1.3.x 7 | - 1.4.x 8 | - 1.5.x 9 | - 1.6.x 10 | - 1.7.x 11 | - 1.8.x 12 | - 1.9.x 13 | - tip 14 | 15 | script: 16 | - go test -v 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: http://goel.io/joe 2 | 3 | #####=== Go ===##### 4 | 5 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 6 | *.o 7 | *.a 8 | *.so 9 | 10 | # Folders 11 | _obj 12 | _test 13 | 14 | # Architecture specific extensions/prefixes 15 | *.[568vq] 16 | [568vq].out 17 | 18 | *.cgo1.go 19 | *.cgo2.c 20 | _cgo_defun.c 21 | _cgo_gotypes.go 22 | _cgo_export.* 23 | 24 | _testmain.go 25 | 26 | *.exe 27 | *.test 28 | *.prof 29 | 30 | .idea 31 | web 32 | env_file.sh 33 | .env 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright <2018> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generic Pool 2 | 3 | [![Build Status](https://travis-ci.org/goctx/generic-pool.svg?branch=master)](https://travis-ci.org/goctx/generic-pool) ![](https://img.shields.io/github/issues/goctx/generic-pool.svg) ![](https://img.shields.io/github/stars/goctx/generic-pool.svg) 4 | 5 | golang通用连接池,管理所有实现了`Closer`接口的资源。 6 | 7 | ## Installation 8 | 9 | ```bash 10 | $ go get github.com/goctx/generic-pool 11 | ``` 12 | 13 | (optional) To run unit tests: 14 | 15 | ```bash 16 | $ cd $GOPATH/src/github.com/goctx/generic-pool 17 | $ go test -v 18 | ``` 19 | 20 | ## ChangeLog 21 | 22 | + 添加超时处理机制,需要实现`GetActiveTime`方法返回最新活跃时间 23 | 24 | ## Get Stated 25 | 26 | ```go 27 | type DemoCloser struct { 28 | Name string 29 | activeAt time.Time 30 | } 31 | 32 | func (p *DemoCloser) Close() error { 33 | fmt.Println(p.Name, "closed") 34 | return nil 35 | } 36 | 37 | func (p *DemoCloser) GetActiveTime() time.Time { 38 | return p.activeAt 39 | } 40 | 41 | // 创建连接池 42 | pool, err := NewGenericPool(0, 10, time.Minute*10, func() (io.Closer, error) { 43 | // 模拟连接延迟 44 | time.Sleep(time.Second) 45 | return &DemoCloser{Name: "test"}, nil 46 | }) 47 | 48 | // 从连接池获取连接 49 | if s, err := pool.Acquire();err != nil { 50 | // 出错了 51 | } else { 52 | // it work 53 | // 回收连接 54 | pool.Release(s) 55 | } 56 | // 不需要的时候关闭连接池 57 | pool.Shutdown() 58 | ``` 59 | -------------------------------------------------------------------------------- /pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | type DemoCloser struct { 12 | Name string 13 | activeAt time.Time 14 | } 15 | 16 | func (p *DemoCloser) Close() error { 17 | fmt.Println(p.Name, "closed") 18 | return nil 19 | } 20 | 21 | func (p *DemoCloser) GetActiveTime() time.Time { 22 | return p.activeAt 23 | } 24 | 25 | func TestNewGenericPool(t *testing.T) { 26 | _, err := NewGenericPool(0, 10, time.Minute*10, func() (Poolable, error) { 27 | time.Sleep(time.Second) 28 | return &DemoCloser{Name: "test"}, nil 29 | }) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | } 34 | 35 | func TestGenericPool_Acquire(t *testing.T) { 36 | pool, err := NewGenericPool(0, 5, time.Minute*10, func() (Poolable, error) { 37 | time.Sleep(time.Second) 38 | name := strconv.FormatInt(time.Now().Unix(), 10) 39 | log.Printf("%s created", name) 40 | // TODO: FIXME &DemoCloser{Name: name}后,pool.Acquire陷入死循环 41 | return &DemoCloser{Name: name, activeAt:time.Now()}, nil 42 | }) 43 | if err != nil { 44 | t.Error(err) 45 | return 46 | } 47 | for i := 0; i < 10; i++ { 48 | s, err := pool.Acquire() 49 | if err != nil { 50 | t.Error(err) 51 | return 52 | } 53 | pool.Release(s) 54 | } 55 | } 56 | 57 | func TestGenericPool_Shutdown(t *testing.T) { 58 | pool, err := NewGenericPool(0, 10, time.Minute*10, func() (Poolable, error) { 59 | time.Sleep(time.Second) 60 | return &DemoCloser{Name: "test"}, nil 61 | }) 62 | if err != nil { 63 | t.Error(err) 64 | return 65 | } 66 | if err := pool.Shutdown(); err != nil { 67 | t.Error(err) 68 | return 69 | } 70 | if _, err := pool.Acquire(); err != ErrPoolClosed { 71 | t.Error(err) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var ( 11 | ErrInvalidConfig = errors.New("invalid pool config") 12 | ErrPoolClosed = errors.New("pool closed") 13 | ) 14 | 15 | type Poolable interface { 16 | io.Closer 17 | GetActiveTime() time.Time 18 | } 19 | 20 | type factory func() (Poolable, error) 21 | 22 | type Pool interface { 23 | Acquire() (Poolable, error) // 获取资源 24 | Release(Poolable) error // 释放资源 25 | Close(Poolable) error // 关闭资源 26 | Shutdown() error // 关闭池 27 | } 28 | 29 | type GenericPool struct { 30 | sync.Mutex 31 | pool chan Poolable 32 | maxOpen int // 池中最大资源数 33 | numOpen int // 当前池中资源数 34 | minOpen int // 池中最少资源数 35 | closed bool // 池是否已关闭 36 | maxLifetime time.Duration 37 | factory factory // 创建连接的方法 38 | } 39 | 40 | func NewGenericPool(minOpen, maxOpen int, maxLifetime time.Duration, factory factory) (*GenericPool, error) { 41 | if maxOpen <= 0 || minOpen > maxOpen { 42 | return nil, ErrInvalidConfig 43 | } 44 | p := &GenericPool{ 45 | maxOpen: maxOpen, 46 | minOpen: minOpen, 47 | maxLifetime: maxLifetime, 48 | factory: factory, 49 | pool: make(chan Poolable, maxOpen), 50 | } 51 | 52 | for i := 0; i < minOpen; i++ { 53 | closer, err := factory() 54 | if err != nil { 55 | continue 56 | } 57 | p.numOpen++ 58 | p.pool <- closer 59 | } 60 | return p, nil 61 | } 62 | 63 | func (p *GenericPool) Acquire() (Poolable, error) { 64 | if p.closed { 65 | return nil, ErrPoolClosed 66 | } 67 | for { 68 | closer, err := p.getOrCreate() 69 | if err != nil { 70 | return nil, err 71 | } 72 | // 如果设置了超时且当前连接的活跃时间+超时时间早于现在,则当前连接已过期 73 | if p.maxLifetime > 0 && closer.GetActiveTime().Add(time.Duration(p.maxLifetime)).Before(time.Now()) { 74 | p.Close(closer) 75 | continue 76 | } 77 | return closer, nil 78 | } 79 | } 80 | 81 | func (p *GenericPool) getOrCreate() (Poolable, error) { 82 | select { 83 | case closer := <-p.pool: 84 | return closer, nil 85 | default: 86 | } 87 | p.Lock() 88 | if p.numOpen >= p.maxOpen { 89 | closer := <-p.pool 90 | p.Unlock() 91 | return closer, nil 92 | } 93 | // 新建连接 94 | closer, err := p.factory() 95 | if err != nil { 96 | p.Unlock() 97 | return nil, err 98 | } 99 | p.numOpen++ 100 | p.Unlock() 101 | return closer, nil 102 | } 103 | 104 | // 释放单个资源到连接池 105 | func (p *GenericPool) Release(closer Poolable) error { 106 | if p.closed { 107 | return ErrPoolClosed 108 | } 109 | p.Lock() 110 | p.pool <- closer 111 | p.Unlock() 112 | return nil 113 | } 114 | 115 | // 关闭单个资源 116 | func (p *GenericPool) Close(closer Poolable) error { 117 | p.Lock() 118 | closer.Close() 119 | p.numOpen-- 120 | p.Unlock() 121 | return nil 122 | } 123 | 124 | // 关闭连接池,释放所有资源 125 | func (p *GenericPool) Shutdown() error { 126 | if p.closed { 127 | return ErrPoolClosed 128 | } 129 | p.Lock() 130 | close(p.pool) 131 | for closer := range p.pool { 132 | closer.Close() 133 | p.numOpen-- 134 | } 135 | p.closed = true 136 | p.Unlock() 137 | return nil 138 | } 139 | --------------------------------------------------------------------------------