2017-08-28 09:37:10 +00:00
|
|
|
package overflow_grpc_pool
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrFullPool is the error when the pool is already full
|
|
|
|
ErrPoolFull = errors.New("grpc pool: Pool is reached maximum capacity.")
|
|
|
|
)
|
|
|
|
|
|
|
|
type pooledInstance struct {
|
|
|
|
clientConn *grpc.ClientConn
|
|
|
|
idleTime time.Time
|
|
|
|
instance interface{}
|
|
|
|
inUse bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type Pool interface {
|
|
|
|
Get() (interface{}, error)
|
|
|
|
Put(i interface{})
|
|
|
|
Capacity() int
|
|
|
|
Available() int
|
|
|
|
Destroy()
|
|
|
|
}
|
|
|
|
|
|
|
|
type pool struct {
|
|
|
|
options *Options
|
|
|
|
instances map[interface{}]*pooledInstance
|
|
|
|
instancesCh chan *pooledInstance
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(o *Options) (Pool, error) {
|
|
|
|
var err error
|
|
|
|
p := &pool{
|
|
|
|
options: o.Validate(),
|
|
|
|
instances: make(map[interface{}]*pooledInstance, o.MaxCapacity),
|
|
|
|
instancesCh: make(chan *pooledInstance, o.MaxCapacity),
|
|
|
|
}
|
|
|
|
|
|
|
|
if 0 < o.MaxIdle {
|
|
|
|
for i := 0; i < o.MaxIdle; i++ {
|
|
|
|
err = p.createInstance()
|
|
|
|
if nil != err {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) createInstance() error {
|
|
|
|
maxCapacity := p.options.MaxCapacity
|
|
|
|
currentInstanceCount := len(p.instances)
|
|
|
|
if currentInstanceCount >= maxCapacity {
|
|
|
|
return ErrPoolFull
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, i, err := p.options.Creators()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pi := &pooledInstance{
|
|
|
|
clientConn: conn,
|
|
|
|
idleTime: time.Now(),
|
|
|
|
instance: i,
|
|
|
|
inUse: false,
|
|
|
|
}
|
|
|
|
p.instances[i] = pi
|
|
|
|
p.instancesCh <- pi
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) destroyInstance(i interface{}) {
|
|
|
|
var err error
|
|
|
|
pi, ok := p.instances[i]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = pi.clientConn.Close()
|
|
|
|
if nil != err {
|
|
|
|
log.Printf("pool: ClientConn close error: %v", err)
|
|
|
|
}
|
|
|
|
delete(p.instances, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) get() (*pooledInstance, error) {
|
|
|
|
var pi *pooledInstance
|
|
|
|
var err error
|
|
|
|
|
|
|
|
avail := len(p.instancesCh)
|
|
|
|
if 0 == avail {
|
|
|
|
if err = p.createInstance(); nil != err {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case pi = <-p.instancesCh:
|
|
|
|
// All good
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
idleTimeout := p.options.IdleTimeout
|
2017-08-29 08:54:51 +00:00
|
|
|
if 1 < len(p.instancesCh) && 0 < idleTimeout && pi.idleTime.Add(idleTimeout).Before(time.Now()) {
|
2017-08-28 09:37:10 +00:00
|
|
|
go p.destroyInstance(pi.instance)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
return pi, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) Get() (interface{}, error) {
|
|
|
|
var err error
|
|
|
|
var pi *pooledInstance
|
|
|
|
if pi, err = p.get(); nil != err {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pi.inUse = true
|
|
|
|
|
|
|
|
return pi.instance, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) Put(i interface{}) {
|
|
|
|
pi, ok := p.instances[i]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
pi.idleTime = time.Now()
|
|
|
|
pi.inUse = false
|
|
|
|
|
|
|
|
select {
|
|
|
|
case p.instancesCh <- pi:
|
|
|
|
// All good
|
|
|
|
default:
|
|
|
|
// channel is full
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) Capacity() int {
|
|
|
|
return cap(p.instancesCh)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) Available() int {
|
|
|
|
return len(p.instancesCh)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pool) Destroy() {
|
|
|
|
close(p.instancesCh)
|
|
|
|
|
|
|
|
for k, _ := range p.instances {
|
|
|
|
p.destroyInstance(k)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|