// NewResourcePool use to create pool for resource func NewResourcePool(address string, factory GroupFactory, initNum, maxCap int, idleTimeout time.Duration, circuitBreaker *circuit.CircuitBreaker) *ResourcePool { if maxCap <= 0 { panic(errors.New("invalid of max capacity")) } var ( resourcePool *ResourcePool err error ) resourcePool = &ResourcePool{ factory: factory, resources: make(chan resourceWrapper, maxCap), idleTimeout: idleTimeout, } for i := 0; i < initNum; i++ { var res Resource res, err = factory() if err != nil { continue } log.Infof(nil, "Init resource %v", res) resourcePool.resources <- resourceWrapper{res, time.Now()} } resourcePool.address = address resourcePool.circuitBreaker = circuitBreaker return resourcePool }
// enqueue is used to do real enqueue op func (r *ResourcePool) enqueue(ctx context.Context, res Resource) { if r.address != res.Address() { log.Errorf(ctx, "FATAL: Conn %s back to wrong pool %s\n", r.address, res.Address()) } start := time.Now() log.Infof(ctx, "Start enqueue, address: %s, av-size: %d\n", res.Address(), len(r.resources)) select { case r.resources <- resourceWrapper{res, time.Now()}: log.Infof(ctx, "Put resource back, address: %s, av-size: %d, waste-time: %d\n", res.Address(), len(r.resources), time.Now().Sub(start).Nanoseconds()) return default: log.Warningf(ctx, "Put resource back failure, because full close it, address: %s, waste-time: %d\n", res.Address(), len(r.resources)) r.closeConn(ctx, res) } }
// Get resource from pool func (r *ResourcePool) get(ctx context.Context, wait bool) (Resource, error) { select { case <-ctx.Done(): return nil, ErrTimeout default: } var ( wrapper resourceWrapper err error ) start := time.Now() for { select { case wrapper = <-r.resources: if time.Now().Sub(wrapper.timeUsed) >= r.idleTimeout { log.Warningf(ctx, "Get idle timeout resource, address: %s, av-size: %d, waste-time: %d\n", wrapper.resource.Address(), len(r.resources), time.Now().Sub(start).Nanoseconds()) r.closeConn(ctx, wrapper.resource) continue } log.Infof(ctx, "Get resouce from pool success, address: %v, av-size: %d, waste-time: %d\n", wrapper.resource.Address(), len(r.resources), time.Now().Sub(start).Nanoseconds()) return wrapper.resource, nil default: log.Warningf(ctx, "Have not enough resource in pool, start create resource, address: %s, av-size: %d\n", r.address, len(r.resources)) var res Resource if res, err = r.factory(); err != nil { log.Errorf(ctx, "create resource failure, error: %v", err) return nil, err } log.Infof(ctx, "Create new resource successed, address: %s", res.Address()) r.enqueue(ctx, res) log.Infof(ctx, "New resource back to queue sucessed, address: %s, av-size: %d", res.Address(), len(r.resources)) continue } } }
func (r *ResourcePool) closeConn(ctx context.Context, resource Resource) { if resource == nil { return } err := resource.RawClose() if err != nil { log.Errorf(ctx, "Close connection but failured, address: %s", r.address) } log.Infof(ctx, "Close resource: %s", resource.Address()) }
// Get is used to get resource form group pool func (p *Group) Get(ctx context.Context) (Resource, error) { pool, _, err := p.selectPool(ctx) if err == nil { return nil, err } res, err := pool.Get(ctx) if err != nil { log.Errorf(ctx, "Select pool failure, error: %v", err) return nil, err } log.Infof(ctx, "Selected pool %s with %d\n", res.Address()) return res, nil }