自營鏈接池工具

剛剛開始寫鏈接池時的一些想法:
一、鏈接池最重要的是在於空閒、忙碌和峯值時鏈接的操做邏輯;
二、鏈接池工具跟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
}
相關文章
相關標籤/搜索