1. 程式人生 > >redigo的redis.Pool 配置引數調優

redigo的redis.Pool 配置引數調優

reids.Pool結構介紹

// github.com/garyburd/redigo/redis/pool.go
type Pool struct {

    // Dial()方法返回一個連線,從在需要建立連線到的時候呼叫
    Dial func() (Conn, error)

    // TestOnBorrow()方法是一個可選項,該方法用來診斷一個連線的健康狀態
    TestOnBorrow func(c Conn, t time.Time) error

    // 最大空閒連線數
    MaxIdle int

    // 一個pool所能分配的最大的連線數目
    // 當設定成0的時候,該pool連線數沒有限制
MaxActive int // 空閒連線超時時間,超過超時時間的空閒連線會被關閉。 // 如果設定成0,空閒連線將不會被關閉 // 應該設定一個比redis服務端超時時間更短的時間 IdleTimeout time.Duration // 如果Wait被設定成true,則Get()方法將會阻塞 Wait bool // mu protects fields defined below. mu sync.Mutex cond *sync.Cond closed bool active int // 空閒連線佇列
idle list.List }

從連線池中獲取連線

// get prunes stale connections and returns a connection from the idle list or
// creates a new connection.
func (p *Pool) get() (Conn, error) {
    p.mu.Lock()

    //修剪idle列表上那些超時的連線 
    if timeout := p.IdleTimeout; timeout > 0 {
        for i, n := 0, p.idle.Len(); i < n; i++ {
e := p.idle.Back()//取得idle列表中的最後一個連線(空閒時間最長) if e == nil { break } ic := e.Value.(idleConn) if ic.t.Add(timeout).After(nowFunc()) { //如果空閒時間最長的連線都沒有超時,則不再修剪 break } p.idle.Remove(e)//從空閒列表中移除這個連線 p.release()//減少p.active,發訊息給阻塞的請求 p.mu.Unlock() ic.c.Close()//關閉連線 p.mu.Lock() } } for { // 從idle列表中獲取一個可用的連線 for i, n := 0, p.idle.Len(); i < n; i++ { e := p.idle.Front()//從idle列表前面取連線,是剛剛使用過的連線 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()//減少p.active,發訊息給阻塞的請求 } // 檢查pool本身有沒有關閉 if p.closed { p.mu.Unlock() return nil, errors.New("redigo: get on closed pool") } // 建立新的連線 if p.MaxActive == 0 || p.active < p.MaxActive {//看看是否沒有到最大的active限制 dial := p.Dial p.active += 1 p.mu.Unlock() c, err := dial()//呼叫dial方法去建立連線 if err != nil { p.mu.Lock() p.release() p.mu.Unlock() c = nil } return c, err } //如果建立連線失敗或者建立達到連線池的限制 if !p.Wait {//不阻塞就直接返回錯誤 p.mu.Unlock() return nil, ErrPoolExhausted } if p.cond == nil { p.cond = sync.NewCond(&p.mu) } p.cond.Wait()//阻塞,等待喚醒 } }

關閉連線

func (p *Pool) put(c Conn, forceClose bool) error {
    err := c.Err()
    p.mu.Lock()
    if !p.closed && err == nil && !forceClose {//p沒有關閉,且沒有錯誤,且不是強制關閉連線
        p.idle.PushFront(idleConn{t: nowFunc(), c: c})// 把返回的連線放到idle的隊首
        if p.idle.Len() > p.MaxIdle {// 如果idle列表的長度過長(至多也只能多1)
            c = p.idle.Remove(p.idle.Back()).(idleConn).c// idle列表的最後一個連線
        } else {
            c = nil
        }
    }

    if c == nil {
        if p.cond != nil {
            p.cond.Signal()//成功放回空閒連線通知其他阻塞的程序
        }
        p.mu.Unlock()
        return nil
    }

    p.release()//減少active計數
    p.mu.Unlock()
    return c.Close()//關閉該連線
}

配置場景

再來看下主要引數

  • MaxIdle
    • 表示連線池空閒連線列表的長度限制
    • 空閒列表是一個棧式的結構,先進後出
  • MaxActive
    • 表示連線池中最大連線數限制
    • 主要考慮到服務端支援的連線數上限,以及應用之間”瓜分”連線數
  • IdleTimeout
    • 空閒連線的超時設定,一旦超時,將會從空閒列表中摘除
    • 該超時時間時間應該小於服務端的連線超時設定

區分兩種使用場景:

  1. 高頻呼叫的場景,需要儘量壓榨redis的效能:
    • 調高MaxIdle的大小,該數目小於maxActive,由於作為一個緩衝區一樣的存在,擴大緩衝區自然沒有問題
    • 調高MaxActive,考慮到服務端的支援上限,儘量調高
    • IdleTimeout由於是高頻使用場景,設定短一點也無所謂,需要注意的一點是MaxIdle設定的長了,佇列中的過期連線可能會增多,這個時候IdleTimeout也要相應變化
  2. 低頻呼叫的場景,呼叫量遠未達到redis的負載,穩定性為重:
    • MaxIdle可以設定的小一些
    • IdleTimeout相應地設定小一些
    • MaxActive隨意,夠用就好,容易檢測到異常