這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
剛剛開始寫串連池時的一些想法:
1、串連池最重要的是在於空閑、忙碌和峰值時串連的操作邏輯;
2、串連池工具跟mysql、redis、tcp、沒有什麼特定的關係,只要生產模式是io.Closer介面即可;
3、串連池多數情況下在串連使用釋放後會進行Rollback,這裡的操作是直接進行Close操作(多數操作是直接進行串連回池複用),但是我這兒考慮到可能服務串連受網路波動,所以乾脆進行Close
4、有一個單獨的協程不斷的在監控串連池中的chan io.Closer不斷進行串連補充、狀態變化(如空閑、忙碌切換)
不多廢話上pool.go代碼
package coreimport ( "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 MysqlPoolfunc 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}//logfunc (this *Pool)log(value ...interface{}) { if this.IsDebug { fmt.Println("[pool]", this.Name, value) }}//set initfunc (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 poolfunc (this *Pool)Back(one io.Closer) error { if one != nil { return one.Close() } return errors.New("back instance is nil")}//get instancefunc (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 mainimport ( _ "github.com/go-sql-driver/mysql" "io" "core")var pool *core.Poolfunction 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 factoryfunc 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}