下面由golang教程栏目给大家介绍Golang连接池的几种实现案例,希望对需要的朋友有所帮助!
因为TCP的三次握手等等原因,建立一个连接是一件成本比较高的行为。所以在一个需要多次与特定实体交互的程序中,就需要维持一个连接池,里面有可以复用的连接可供重复使用。
而维持一个连接池,最基本的要求就是要做到:thread safe(线程安全),尤其是在Golang这种特性是goroutine的语言中。
实现简单的连接池
type Pool struct { m sync.Mutex //保证多个goroutine访问时候,closed的线程安全 res chan io.Closer //连接存储的chan factory func() (io.Closer,error) //新建连接的工厂方法 closed bool //连接池关闭标志 }
这个简单的连接池,我们利用chan来存储池里的连接。而新建结构体的方法也比较简单:
func New(fn func() (io.Closer, error), size uint) (*Pool, error) { if size <= 0 { return nil, errors.New("size的值太小了。") } return &Pool{ factory: fn, res: make(chan io.Closer, size), }, nil }
只需要提供对应的工厂函数和连接池的大小就可以了。
获取连接
那么我们要怎么从中获取资源呢?因为我们内部存储连接的结构是chan,所以只需要简单的select就可以保证线程安全:
//从资源池里获取一个资源 func (p *Pool) Acquire() (io.Closer,error) { select { case r,ok := <-p.res: log.Println("Acquire:共享资源") if !ok { return nil,ErrPoolClosed } return r,nil default: log.Println("Acquire:新生成资源") return p.factory() } }
我们先从连接池的res这个chan里面获取,如果没有的话我们就利用我们早已经准备好的工厂函数进行构造连接。同时我们在从res获取连接的时候利用ok先确定了这个连接池是否已经关闭。如果已经关闭的话我们就返回早已经准备好的连接已关闭错误。
关闭连接池
那么既然提到关闭连接池,我们是怎么样关闭连接池的呢?
//关闭资源池,释放资源 func (p *Pool) Close() { p.m.Lock() defer p.m.Unlock() if p.closed { return } p.closed = true //关闭通道,不让写入了 close(p.res) //关闭通道里的资源 for r:=range p.res { r.Close() } }
这边我们需要先进行p.m.Lock()*上锁操作,这么做是因为我们需要对结构体里面的*closed进行读写。需要先把这个标志位设定后,关闭res这个chan,使得Acquire方法无法再获取新的连接。我们再对res这个chan里面的连接进行Close操作。
释放连接
释放连接首先得有个前提,就是连接池还没有关闭。如果连接池已经关闭再往res里面送连接的话就好触发panic。
func (p *Pool) Release(r io.Closer){ //保证该操作和Close方法的操作是安全的 p.m.Lock() defer p.m.Unlock() //资源池都关闭了,就省这一个没有释放的资源了,释放即可 if p.closed { r.Close() return } select { case p.res <- r: log.Println("资源释放到池子里了") default: log.Println("资源池满了,释放这个资源吧") r.Close() } }