刚刚开始写连接池时的一些想法:
1、连接池最重要的是在于空闲、忙碌和峰值时连接的操作逻辑;
2、连接池工具跟mysql、redis、tcp、没有什么特定的关系,只要生产模式是io.Closer接口即可;
3、连接池多数情况下在连接使用释放后会进行Rollback,这里的操作是直接进行Close操作(多数操作是直接进行连接回池复用),但是我这儿考虑到可能服务连接受网络波动,所以干脆进行Close?
4、有一个单独的协程不断的在监控连接池中的chan io.Closer不断进行连接补充、状态变化(如空闲、忙碌切换)
不多废话上pool.go代码
package core
import (
"errors"
"time"
"sync"
"io"
"fmt"
)
//连接池生产
type PoolFactory func() (io.Closer, error)
//连接池管理实例
type Pool struct {
mu sync.Mutex
MaxIdle int
MaxOpen int
IsDebug bool
Name string
busy bool
factory PoolFactory
stack chan io.Closer
}
//new MysqlPool
func NewPool(factory PoolFactory, maxIdle int, maxOpen int, name string) *Pool {
pool := new(Pool)
pool.Name = name
pool.factory = factory
pool.setInit(maxIdle, maxOpen)
return pool
}
//log
func (this *Pool)log(value ...interface{}) {
if this.IsDebug {
fmt.Println("[pool]", this.Name, value)
}
}
//set init
func (this *Pool)setInit(maxIdle int, maxOpen int) error {
if maxOpen < maxIdle {
return errors.New("maxOpen can not less than maxIdle")
}
this.stack = make(chan io.Closer, maxOpen)
go func() {
for {
busyState := this.busy && len(this.stack) < maxOpen
idleState := len(this.stack) < maxIdle
if maxIdle <= 0 || busyState || idleState {
one, err := this.factory()
if err == nil {
this.stack <- one
}
this.log("create one", len(this.stack))
}
time.Sleep(time.Microsecond * 10)
}
}()
return nil
}
//back to pool
func (this *Pool)Back(one io.Closer) error {
if one != nil {
return one.Close()
}
return errors.New("back instance is nil")
}
//get instance
func (this *Pool)Get() (io.Closer, error) {
this.mu.Lock()
defer this.mu.Unlock()
if this.MaxIdle > 0 && len(this.stack) < this.MaxIdle {
this.busy = true
} else {
this.busy = false
}
select {
case one := <-this.stack:
this.log("use one")
return one, nil
case <-time.After(time.Microsecond * 10):
this.busy = true
return nil, errors.New("pool timeout")
}
}
如何使用连接池呢?灰常简单
这里使用了gorm来创建mysql连接的方式进行facotry pool工厂创建
package main
import (
_ "github.com/go-sql-driver/mysql"
"io"
"core"
)
var pool *core.Pool
function main(){
pool = core.NewPool(poolMysqlFactory, 5, 50, "mysql")
pool.IsDebug = true
//use pool
err,conn := pool.Get()
//balabala use conn
conn.Close()
time.Sleep(time.Hour*1)
}
//mysql pool factory
func poolMysqlFactory() (io.Closer, error) {
conn, err := gorm.Open("mysql", "username:password@tcp(localhost:3306)/test?charset=utf8&parseTime=True&loc=Local")
if err != nil {
return nil, err
}
return conn, nil
}