golang 创建DB连接池

使用通道

import(
    "database/sql"
    _"github.com/go-sql-driver/mysql"
    "log"
    "time"
    "math/rand"
)
// 连接池大小
var MAX_POOL_SIZE = 20
var dbPoll chan *sql.DB

const (
    user="root"
    pass="root"
    db="school"

)
func putDB(db *sql.DB) {
    // 基于函数和接口间互不信任原则,这里再判断一次,养成这个好习惯哦
    if dbPoll == nil {
        dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
    }
    if len(dbPoll) >= MAX_POOL_SIZE {
        db.Close()
        return
    }
    dbPoll <- db
}
func initDB() {
    // 缓冲机制,相当于消息队列
    if len(dbPoll) == 0 {
        // 如果长度为0,就定义一个redis.Conn类型长度为MAX_POOL_SIZE的channel
        dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
        go func() {
            for i := 0; i < MAX_POOL_SIZE/2; i++ {
                db,err:=sql.Open("mysql",user+":"+pass+"@tcp(localhost:3306)/"+db+"?charset=utf8")
                if err!=nil {
                    log.Println(err)
                }
                putDB(db)
            }
        } ()
    }
}
func GetDB()  *sql.DB {
    //如果为空就初始化或者长度为零
    if dbPoll == nil||len(dbPoll) == 0{
        initDB()
    }
    return <- dbPoll
}

自己写的连接池

db.go

package db

import (
    "time"
    "github.com/Sirupsen/logrus"
    "github.com/jinzhu/gorm"
    _"github.com/go-sql-driver/mysql"
    "reflect"
    "fmt"
    "errors"
    "strconv"
    "youguoju/conf/config"
)

type dB interface {
    Gorm()*gorm.DB
    Id()uint32//ID的获取方法
}

type myDB struct {
    db *gorm.DB
    id uint32//ID
}

type DBPoll interface {
    Take()(dB,error)//取出实体
    Return(entity dB)(error)//归还实体
    Total()uint32//实体的容量
    Used()uint32//实体中已经被使用的实体数量
}
type myDBPoll struct {
    pool  DBPoll //实体池
    etype reflect.Type    //池内实体的类型
}
//生成DB的函数类型
type genDB func() dB
func newDBPoll(total uint32,gen genDB)(DBPoll,error)  {
    etype:=reflect.TypeOf(gen())
    genEntity:= func() dB{return gen()}
    pool,err:= NewPool(total,etype,genEntity)
    if err!=nil {
        return nil,err
    }
    dbpool:=&myDBPoll{pool,etype}
    return dbpool,nil
}

func (db *myDB)Id()uint32  {
    return db.id
}
func (db *myDB)Gorm()*gorm.DB {
    return db.db
}
//取出实体
func (pool *myDBPoll)Take()(dB,error){
    entity,err:=pool.pool.Take()
    if err!=nil {
        return nil,err
    }
    dl,ok:=entity.(dB)//强制类型转换
    if !ok {
        errMsg:=fmt.Sprintf("The type of entity id NOT %s\n",pool.etype)
        panic(errors.New(errMsg))
    }
    return dl,nil
}
//归还实体
func (pool *myDBPoll)Return(entity dB)(error){
    return pool.pool.Return(entity)
}
//实体的容量
func (pool *myDBPoll)Total()uint32{
    return pool.pool.Total()
}
//实体中已经被使用的实体数量
func (pool *myDBPoll)Used()uint32{
    return pool.pool.Used()
}

var dbPoll DBPoll

func InitDB() {

    total := config.Conf.Total
    to,_:=strconv.Atoi(total)
    dbPoll,_=newDBPoll(uint32(to),initDb)
}
//func GetDBPollInstance() DBPoll {
//  return dbPoll
//}
func GetDBInstance() (dB,error) {
    db,err:=dbPoll.Take()
    if err!=nil {
        return nil, err
    }
    return db,nil
}
func ReturnDB(db dB) error {
    return dbPoll.Return(db)
}
func initDb()  dB{
    var db *gorm.DB
    var err error
    path := config.Conf.DBURL               //从env获取数据库连接地址
    logrus.Info("path:", string(path)) //打印数据库连接地址
    for {
        db, err = gorm.Open("mysql", string(path)) //使用gorm连接数据库
        if err != nil {
            logrus.Error(err, "Retry in 2 seconds!")
            time.Sleep(time.Second * 2)
            continue
        }
        logrus.Info("DB connect successful!")
        break
    }
    return &myDB{db:db,id:idGenertor.GetUint32()}
}
var idGenertor IdGenertor = NewIdGenertor()

pool.go

package db


import (
"reflect"
"fmt"
"errors"
"sync"
)

type Pool interface {
    Take()(dB,error)//取出实体
    Return(entity dB)(error)//归还实体
    Total()uint32//实体的容量
    Used()uint32//实体中已经被使用的实体数量
}
////实体的接口类型
//type Entity1 interface {
//  Id()uint32//ID的获取方法
//}

//实体池的实现类型
type myPool struct {
    total uint32//池的总容量
    etype reflect.Type//池中实体的类型
    genEntity func()dB//池中实体的生成函数
    container chan dB//实体容器
    //实体Id的容器
    idContainer map[uint32]bool
    mutex sync.Mutex
}

func NewPool(total uint32,entityType reflect.Type,genEntity func()dB)(Pool,error)  {
    if total==0 {
        errMsg:=fmt.Sprintf("The pool can not be initialized! (total=%d)\n",total)
        return nil,errors.New(errMsg)
    }
    size:=int(total)
    container:=make(chan dB,size)
    idContainer:=make(map[uint32]bool)
    for i:=0;i<size ; i++ {
        newEntity:=genEntity()
        if entityType!=reflect.TypeOf(newEntity) {
            errMsg:=fmt.Sprintf("The type of result of function gen Entity()is Not %s\n",entityType)
            return nil,errors.New(errMsg)
        }
        container<-newEntity
        idContainer[newEntity.Id()]=true
    }
    pool:=&myPool{total,entityType,genEntity,container,idContainer,*new(sync.Mutex)}
    return pool,nil
}
//取出实体
func (pool *myPool)Take()(dB,error){
    entity,ok:=<-pool.container
    if !ok {
        return nil,errors.New("The innercontainer is invalid")
    }
    pool.mutex.Lock()
    defer pool.mutex.Unlock()
    pool.idContainer[entity.Id()]=false
    return  entity,nil
}
//归还实体
func (pool *myPool)Return(entity dB)(error){
    if entity==nil {
        return errors.New("The returning entity is invalid")
    }
    if pool.etype!=reflect.TypeOf(entity) {
        errMsg:=fmt.Sprintf("The type of result of function gen Entity()is Not %s\n",pool.etype)
        return errors.New(errMsg)
    }
    entityId:=entity.Id()
    caseResult:=pool.compareAndSetForIdContainer(entityId,false,true)
    if caseResult==-1 {
        errMsg:=fmt.Sprintf("The entity(id=%d) is illegal!\n",entity.Id())
        return errors.New(errMsg)
    }
    if caseResult==0 {
        errMsg:=fmt.Sprintf("The entity(id=%d) is already in the pool!\n",entity.Id())
        return errors.New(errMsg)
    }else {
        pool.idContainer[entityId]=true
        pool.container<-entity
        return nil
    }
}
//比较并设置实体ID容器中与给定实体ID对应的键值对的元素值
//结果值;1操作成功
//      0.对应的id在容器中已经存在
//      -1.对应的id在容器中不存在
//
func (pool *myPool) compareAndSetForIdContainer(entityId uint32,oldValue,newValue bool)int8  {
    pool.mutex.Lock()
    defer pool.mutex.Unlock()
    v,ok:=pool.idContainer[entityId]
    if !ok {
        return -1
    }
    if v!=oldValue {
        return 0
    }
    pool.idContainer[entityId]=newValue
    return 1
}
//实体的容量
func (pool *myPool)Total()uint32{
    return uint32(cap(pool.container))
}
//实体中已经被使用的实体数量
func (pool *myPool)Used()uint32{

    return uint32(cap(pool.container)-len(pool.container))
}

id生成器

package db

import (
    "sync"
    "math"
)

type IdGenertor interface {
    GetUint32() uint32//获取一个unit32类型的Id
}
type cyclicIdGenertor struct {
    id uint32//当前id
    ended bool//签一个id是否为其类型所能表示的做大值
    mutex sync.Mutex
}

func NewIdGenertor() IdGenertor {
    return &cyclicIdGenertor{}
}
//获取一个unit32类型的Id
func (gen *cyclicIdGenertor)GetUint32() uint32 {
    gen.mutex.Lock()
    defer gen.mutex.Unlock()
    if gen.ended {
        defer func() {gen.ended=false}()
        gen.id=uint32(1)
        return uint32(0)
    }
    id:=gen.id
    if  id<math.MaxInt32{
        gen.id++
    }else {
        gen.ended=true
    }
    return id
}
type cyclicIdGenertor2 struct {
    base cyclicIdGenertor//基本id生成器
    cycleCount uint64//基于unit32类型的取值范围的周期计数
}
//获取一个unit64类型的Id
func (gen *cyclicIdGenertor2)GetUint64() uint64{
    var id64 uint64
    if gen.cycleCount%2==1 {
        id64+=math.MaxUint32
    }
    id32:=gen.base.GetUint32()
    if id32==math.MaxInt32 {
        gen.cycleCount++
    }
    id64+=uint64(id32)
    return id64
}

使用

//利用token从数据库库中获取用户信息
func (user *User)GetUserByTokenFromDB(token string)(bool)  {
    instance,err:=db.GetDBInstance()
    defer db.ReturnDB(instance)
    if err!=nil {
        return false
    }
    gorm:=instance.Gorm()
    gorm.Where("Token = ?", token).Find(&user)
    return true
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • database/sql database/sql是golang的标准库之一,它提供了一系列接口方法,用于访问关系...
    人世间阅读 39,103评论 5 55
  • 这些属性是否生效取决于对应的组件是否声明为 Spring 应用程序上下文里的 Bean(基本是自动配置的),为一个...
    发光的鱼阅读 1,416评论 0 14
  • application的配置属性。 这些属性是否生效取决于对应的组件是否声明为Spring应用程序上下文里的Bea...
    新签名阅读 5,352评论 1 27
  • 从小到大,记不得多少个夏夜,村子里偶尔会停电,受不住热天的我,热的满头大汗被蚊子叮醒,然后每次妈妈发现后她...
    赌w阅读 185评论 1 2