這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
結構體分析
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() }}