刚刚开始写连接池时的一些想法:
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
}

gofounder
29 声望6 粉丝

tech man...