剛剛開始寫鏈接池時的一些想法:
一、鏈接池最重要的是在於空閒、忙碌和峯值時鏈接的操做邏輯;
二、鏈接池工具跟mysql、redis、tcp、沒有什麼特定的關係,只要生產模式是io.Closer接口便可;
三、鏈接池多數狀況下在鏈接使用釋放後會進行Rollback,這裏的操做是直接進行Close操做(多數操做是直接進行鏈接回池複用),可是我這兒考慮到可能服務鏈接受網絡波動,因此乾脆進行Close?
四、有一個單獨的協程不斷的在監控鏈接池中的chan io.Closer不斷進行鏈接補充、狀態變化(如空閒、忙碌切換)mysql
很少廢話上pool.go代碼git
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工廠建立github
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 }