go通用连接池的实现
简介
最近写项目经常需要用到连接池来管理各种连接,连接池的好处在于对频繁的请求不必单独创建连接资源,减少cpu及内存消耗。
线程安全
由于获取连接的请求总是并发的,所以需要考虑到线程安全的问题。其他语言的连接池在获取连接时需要加锁来实现共享资源也就是连接池的同步。而go语言却利用信道,将共享的资源放在信道上进行传输,同一时刻只能有一个goroutine拥有该信道。这也是go提倡的一种方式,这种方式编写的代码简洁且效率高。
Do not communicate by sharing memory; instead , share memory by communicating.
代码实现
下面给出一个通用的go语言连接池的实现
type ConnRes interface {
Close() error
}
type Factory func() (ConnRes,error)
type Pool struct {
conns chan ConnRes
factory Factory
}
func NewPool(factory Factory,cap int) *Pool{
return &Pool{
conns:make(chan ConnRes,cap),
factory:factory,
}
}
func (p *Pool)new() (ConnRes,error){
return p.factory()
}
func (p *Pool) Get() (conn ConnRes){
select {
case conn = <- p.conns:{}
default:
conn, _ = p.new()
}
return
}
func (p *Pool) Put(conn ConnRes){
select {
case p.conns <- conn:{}
default:
conn.Close()
}
}
go语言的可组合性非常强,如上的代码用简单的函数对象就能创建不同的连接资源类型。
创建实例
一个连接池的创建可能是这样的
func main(){
p := NewPool(func() (ConnRes,error) {
return net.Dial("tcp",":8080")
},10)
}
类似于工厂模式,我们不需要明确知道连接类型,只要给定创建该连接类型的函数,我们便可以随时调用该函数创建连接。
总结
1 接口
go语言的接口与其他语言的接口意义相同,作为某一种类型的规范或者约定,但go不需要显式的指定哪个类准从哪一类规范,该类只需要实现该种接口规范的所有方法即可作为该接口的实现。当然接口也可以作为实现接口的引用,只不过最终都是由类或者结构体来实现。
2 信道
go语言中的信道是一个高效的用于线程通信的功能,go语言不提倡用线程通信来实现共享内存,因为大多数时候我们不需要共享内存,我们需要的是线程的同步和通信,所以我们直接使用信道。
3 select
select与信道是密不可分的,运用selec会随机选择一个可执行的case进行执行,该原理可能很复杂,信道的很多参数也与select相关。具体实现原理待分析。
4 函数变量
go语言的函数也可以看做一种类型,该类型可作为参数进行传递,在与interface结合使用时有很大的灵活性