golang通用鏈接池的實現

golang的channel除了goroutine通訊以外還有不少其餘的功能,本文將實現一種基於channel的通用鏈接池。git

更新

  • 添加超時處理,須要實現GetActiveTime方法獲取最後活躍時間。

何爲通用?

鏈接池的實現不依賴具體的實例,而依賴某個接口,本文的鏈接池選用的是io.Closer接口,只要是實現了該接口的對象均可以被池管理。
固然,你能夠實現基於interface{}的鏈接池,這樣任何對象均可以被管理。github

實現原理

將鏈接句柄存入channel中,因爲緩存channel的特性,獲取鏈接時若是池中有鏈接,將直接返回,若是池中沒有鏈接,將阻塞或者新建鏈接(沒超過最大限制的狀況下)。
因爲面向接口編程,全部建立鏈接的邏輯是不清楚的,這裏須要傳入一個函數,該函數返回一個io.Closer對象。golang

實現

因爲併發問題,在須要操做池中互斥數據的時候須要加鎖。redis

package pool

import (
    "errors"
    "io"
    "sync"
    "time"
)

var (
    ErrInvalidConfig = errors.New("invalid pool config")
    ErrPoolClosed    = errors.New("pool closed")
)

type Poolable interface {
    io.Closer
    GetActiveTime() time.Time
}

type factory func() (Poolable, error)

type Pool interface {
    Acquire() (Poolable, error) // 獲取資源
    Release(Poolable) error     // 釋放資源
    Close(Poolable) error       // 關閉資源
    Shutdown() error            // 關閉池
}

type GenericPool struct {
    sync.Mutex
    pool        chan Poolable
    maxOpen     int  // 池中最大資源數
    numOpen     int  // 當前池中資源數
    minOpen     int  // 池中最少資源數
    closed      bool // 池是否已關閉
    maxLifetime time.Duration
    factory     factory // 建立鏈接的方法
}

func NewGenericPool(minOpen, maxOpen int, maxLifetime time.Duration, factory factory) (*GenericPool, error) {
    if maxOpen <= 0 || minOpen > maxOpen {
        return nil, ErrInvalidConfig
    }
    p := &GenericPool{
        maxOpen:     maxOpen,
        minOpen:     minOpen,
        maxLifetime: maxLifetime,
        factory:     factory,
        pool:        make(chan Poolable, maxOpen),
    }

    for i := 0; i < minOpen; i++ {
        closer, err := factory()
        if err != nil {
            continue
        }
        p.numOpen++
        p.pool <- closer
    }
    return p, nil
}

func (p *GenericPool) Acquire() (Poolable, error) {
    if p.closed {
        return nil, ErrPoolClosed
    }
    for {
        closer, err := p.getOrCreate()
        if err != nil {
            return nil, err
        }
        // 若是設置了超時且當前鏈接的活躍時間+超時時間早於如今,則當前鏈接已過時
        if p.maxLifetime > 0 && closer.GetActiveTime().Add(p.maxLifetime).Before(time.Now()) {
            p.Close(closer)
            continue
        }
        return closer, nil
    }
}

func (p *GenericPool) getOrCreate() (Poolable, error) {
    select {
    case closer := <-p.pool:
        return closer, nil
    default:
    }
    p.Lock()
    if p.numOpen >= p.maxOpen {
        closer := <-p.pool
        p.Unlock()
        return closer, nil
    }
    // 新建鏈接
    closer, err := p.factory()
    if err != nil {
        p.Unlock()
        return nil, err
    }
    p.numOpen++
    p.Unlock()
    return closer, nil
}

// 釋放單個資源到鏈接池
func (p *GenericPool) Release(closer Poolable) error {
    if p.closed {
        return ErrPoolClosed
    }
    p.Lock()
    p.pool <- closer
    p.Unlock()
    return nil
}

// 關閉單個資源
func (p *GenericPool) Close(closer Poolable) error {
    p.Lock()
    closer.Close()
    p.numOpen--
    p.Unlock()
    return nil
}

// 關閉鏈接池,釋放全部資源
func (p *GenericPool) Shutdown() error {
    if p.closed {
        return ErrPoolClosed
    }
    p.Lock()
    close(p.pool)
    for closer := range p.pool {
        closer.Close()
        p.numOpen--
    }
    p.closed = true
    p.Unlock()
    return nil
}

結論

基於該鏈接池,能夠管理全部io.Closer對象。好比memcached,redis等等,很是方便!編程

項目地址

https://github.com/goctx/generic-pool緩存

相關文章
相關標籤/搜索