redigo 鏈接池代碼分析

結構體分析

type Pool struct {

    // 用來建立redis鏈接的方法
    Dial func() (Conn, error)

    // 若是設置了給func,那麼每次p.Get()的時候都會調用改方法來驗證鏈接的可用性
    TestOnBorrow func(c Conn, t time.Time) error

    // 定義鏈接池中最大鏈接數(超過這個數會關閉老的連接,總會保持這個數)
    MaxIdle int

    // 當前鏈接池中可用的連接數.
    MaxActive int

    // 定義連接的超時時間,每次p.Get()的時候會檢測這個鏈接是否超時(超時會關閉,並釋放可用鏈接數).
    IdleTimeout time.Duration

    // 當可用鏈接數爲0是,那麼當wait=true,那麼當調用p.Get()時,會阻塞等待,不然,返回nil.
    Wait bool

    // 讀寫鎖控制.
    mu     sync.Mutex
    // 用來條件控制,這裏主要是當連接被關閉時,提醒在等待的進程可使用了,或者能夠自行建立了
    cond   *sync.Cond
    // 當前鏈接池是否已經關閉
    closed bool
    // 當前可用的連接數
    active int

    // 連接存儲在一個棧中.
    idle list.List
}

鏈接池關閉方法

func (p *Pool) Close() error {
    p.mu.Lock()
    // 獲取鏈接池全部連接棧
    idle := p.idle
    // 從新初始化
    p.idle.Init()
    // 標示已經關閉
    p.closed = true
    // 控制可用鏈接數
    p.active -= idle.Len()
    // 若是當前有進程正在等待獲取的話,則通知能夠獲取或者自行建立
    if p.cond != nil {
        p.cond.Broadcast()
    }
    p.mu.Unlock()
    // 遍歷棧,逐個關閉連接
    for e := idle.Front(); e != nil; e = e.Next() {
        e.Value.(idleConn).c.Close()
    }
    return nil
}

釋放一個連接

func (p *Pool) release() {
    // 當連接超時,或者ping不通,或者建立失敗,則當即使用連接表示
    p.active -= 1
    // 若是已經有進程在以前等待了,則通知其使用或者自行建立
    if p.cond != nil {
        p.cond.Signal()
    }
}

關閉鏈接

func (p *Pool) put(c Conn, forceClose bool) error {
    err := c.Err()
    p.mu.Lock()
    // 若是鏈接池沒有關閉,而且不是強制關閉的
    if !p.closed && err == nil && !forceClose {
        // 把指定的連接放在空閒棧首位
        p.idle.PushFront(idleConn{t: nowFunc(), c: c})
        // 若是棧的長度大於指定長度,則吧最後一個(可能超時)剔除
        if p.idle.Len() > p.MaxIdle {
            c = p.idle.Remove(p.idle.Back()).(idleConn).c
        } else {
            c = nil
        }
    }

    if c == nil {
        //成功放回空閒鏈接通知其餘阻塞的進程
        if p.cond != nil {
            p.cond.Signal()
        }
        p.mu.Unlock()
        return nil
    }
    // 減小active計數(感受這裏能夠不用處理,上面若是是替換的話)
    p.release()
    p.mu.Unlock()
    // 關閉鏈接
    return c.Close()
}

獲取連接

func (p *Pool) get() (Conn, error) {
    p.mu.Lock()
    // 處理舊的連接(檢測是否超時)
    if timeout := p.IdleTimeout; timeout > 0 {
        for i, n := 0, p.idle.Len(); i < n; i++ {
            e := p.idle.Back()
            if e == nil {
                break
            }
            ic := e.Value.(idleConn)
            // 連接建立的時間加上超時時間是否小於當前時間
            if ic.t.Add(timeout).After(nowFunc()) {
                break
            }
            // 超時連接,從棧中刪除
            p.idle.Remove(e)
            // 可用鏈接數減小,並通知其餘等待的進程處理
            p.release()
            p.mu.Unlock()
            // 關閉當前鏈接
            ic.c.Close()
            p.mu.Lock()
        }
    }
    for {

        // 從鏈接棧列表中獲取一個可用連接
        for i, n := 0, p.idle.Len(); i < n; i++ {
            // 從idle列表前面取鏈接,那麼必然是剛剛使用過的鏈接
            e := p.idle.Front()
            if e == nil {
                break
            }
            // 類型斷言
            ic := e.Value.(idleConn)
            // 從棧中刪除
            p.idle.Remove(e)
            test := p.TestOnBorrow
            p.mu.Unlock()
            // 經過使用校驗連接函數檢測連接
            if test == nil || test(ic.c, ic.t) == nil {
                return ic.c, nil
            }
            // 檢驗出問題,關閉鏈接
            ic.c.Close()
            p.mu.Lock()
            // 下降可用鏈接數,並通知其餘進程處理
            p.release()
        }

        // 檢測鏈接池是否已經關閉.
        if p.closed {
            p.mu.Unlock()
            return nil, errors.New("redigo: get on closed pool")
        }

        // 若是可用鏈接數爲0 或者小於最大可用可用鏈接數範圍,那麼建立
        if p.MaxActive == 0 || p.active < p.MaxActive {
            dial := p.Dial
            p.active += 1
            p.mu.Unlock()
            // 鏈接redis server
            c, err := dial()
            // 鏈接失敗關閉之
            if err != nil {
                p.mu.Lock()
                p.release()
                p.mu.Unlock()
                c = nil
            }
            return c, err
        }
        // 若是沒有配置等待,那麼就返回nil
        if !p.Wait {
            p.mu.Unlock()
            return nil, ErrPoolExhausted
        }
        // 若是配置了等待,那麼初始化,而且開始等待,直到有進程通知連接數夠了或者能夠建立了
        if p.cond == nil {
            p.cond = sync.NewCond(&p.mu)
        }
        p.cond.Wait()
    }
}
相關文章
相關標籤/搜索