golang的channel除了goroutine通信之外还有很多其他的功能,本文将实现一种基于channel的通用连接池。
何为通用?
连接池的实现不依赖具体的实例,而依赖某个接口,本文的连接池选用的是io.Closer接口,只要是实现了该接口的对象都可以被池管理。
当然,你可以实现基于interface{}的连接池,这样任何对象都可以被管理。
实现原理
将连接句柄存入channel中,由于缓存channel的特性,获取连接时如果池中有连接,将直接返回,如果池中没有连接,将阻塞或者新建连接(没超过最大限制的情况下)。
由于面向接口编程,所有创建连接的逻辑是不清楚的,这里需要传入一个函数,该函数返回一个io.Closer对象。
实现
由于并发问题,在需要操作池中互斥数据的时候需要加锁。
-
package pool
-
-
import (
-
"errors"
-
"io"
-
"sync"
-
"time"
-
)
-
-
var (
-
ErrInvalidConfig = errors.New("invalid pool config")
-
ErrPoolClosed = errors.New("pool closed")
-
)
-
-
type factory func() (io.Closer, error)
-
-
type Pool interface {
-
Acquire() (io.Closer, error) // 获取资源
-
Release(io.Closer) error // 释放资源
-
Close(io.Closer) error // 关闭资源
-
Shutdown() error // 关闭池
-
}
-
-
type GenericPool struct {
-
sync.Mutex
-
pool chan io.Closer
-
maxOpen int // 池中最大资源数
-
numOpen int // 当前池中资源数
-
minOpen int // 池中最少资源数
-
closed bool // 池是否已关闭
-
maxLifetime time.Duration
-
factory factory // 创建连接的方法
-
}
-
-
func NewGenericPool(minOpen, maxOpen int, maxLifetime time.Duration, factory factory) (*GenericPool, error) {
-
if maxOpen <= 0 || minOpen > maxOpen {
-
return nil, ErrInvalidConfig
-
}
-
p := &GenericPool{
-
maxOpen: maxOpen,
-
minOpen: minOpen,
-
maxLifetime: maxLifetime,
-
factory: factory,
-
pool: make(chan io.Closer, maxOpen),
-
}
-
-
for i := 0; i < minOpen; i++ {
-
closer, err := factory()
-
if err != nil {
-
continue
-
}
-
p.numOpen++
-
p.pool <- closer
-
}
-
return p, nil
-
}
-
-
func (p *GenericPool) Acquire() (io.Closer, error) {
-
if p.closed {
-
return nil, ErrPoolClosed
-
}
-
for {
-
closer, err := p.getOrCreate()
-
if err != nil {
-
return nil, err
-
}
-
// todo maxLifttime处理
-
return closer, nil
-
}
-
}
-
-
func (p *GenericPool) getOrCreate() (io.Closer, error) {
-
select {
-
case closer := <-p.pool:
-
return closer, nil
-
default:
-
}
-
p.Lock()
-
if p.numOpen >= p.maxOpen {
-
closer := <-p.pool
-
p.Unlock()
-
return closer, nil
-
}
-
// 新建连接
-
closer, err := p.factory()
-
if err != nil {
-
p.Unlock()
-
return nil, err
-
}
-
p.numOpen++
-
p.Unlock()
-
return closer, nil
-
}
-
-
// 释放单个资源到连接池
-
func (p *GenericPool) Release(closer io.Closer) error {
-
if p.closed {
-
return ErrPoolClosed
-
}
-
p.Lock()
-
p.pool <- closer
-
p.Unlock()
-
return nil
-
}
-
-
// 关闭单个资源
-
func (p *GenericPool) Close(closer io.Closer) error {
-
p.Lock()
-
closer.Close()
-
p.numOpen--
-
p.Unlock()
-
return nil
-
}
-
-
// 关闭连接池,释放所有资源
-
func (p *GenericPool) Shutdown() error {
-
if p.closed {
-
return ErrPoolClosed
-
}
-
p.Lock()
-
close(p.pool)
-
for closer := range p.pool {
-
closer.Close()
-
p.numOpen--
-
}
-
p.closed = true
-
p.Unlock()
-
return nil
-
}
复制代码
结论
基于该连接池,可以管理所有io.Closer对象。比如memcached,redis等等,非常方便!
Golang语言社区--社区网址:www.golang.ltd