go接続プールの実装
4479 ワード
本文は主に抜粋するhttps://github.com/garyburd/redigo.gitで使用したredis接続プールのソースコードを分析し、詳細は以下の通りです.
接続プールオブジェクト構造体
変数
インタフェースソース分析
Get()接続プールでの接続の取得
Put()空き接続を接続プールに戻す
空き接続をプールに戻すと、
Close()接続プールを閉じる
ActiveCount()アクティブな接続数を取得
release()接続オブジェクトの消去
Activeが減少すると、ここの
プールの接続に使用する関連標準ライブラリ
以上のgo実装の接続プールは参考にして使用することができて、Connインタフェースのタイプを修正したり交換したりすればいいので、問題があれば、皆さんの伝言を歓迎します.
接続プールオブジェクト構造体
type Conn interface {
Close()
}
type idleConn struct {
c Conn
t time.Time //
}
type ConnPool struct {
//
Dial func() (Conn, error)
//
TestOnBorrow func(c Conn, t time.Time) error
MaxActive int //
MaxIdle int //
IdleTimeout time.Duration //
Wait bool
mu sync.Mutex
cond *sync.Cond //
closed bool //
active int //
idle list.List // , idleConn
}
変数
wait
の役割:デフォルトwaitがfalseの場合、接続プールの接続オブジェクトの個数が許容最大数に達した場合、cond条件変数が信号通知を受信するまでgoroutineをブロックし、接続オブジェクトを取得し続ける.waitがtrueの場合、接続プールがいっぱいになったエラーメッセージを返します.インタフェースソース分析
Get()接続プールでの接続の取得
func (p *ConnPool) 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(time.Now()) {
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++ {
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("get on closed pool")
}
//
if p.MaxActive == 0 || p.active < p.MaxActive {
dial := p.Dial
p.active += 1
p.mu.Unlock()
c, err := 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, errors.New("connection pool exhausted")
}
if p.cond == nil {
p.cond = sync.NewCond(&p.mu)
}
p.cond.Wait() //
}
}
p.cond.Wait()
:関数実行プロセスのロック解除--通知信号の取得を待つ--再ロックするため、この関数を使用する場合は先にロックする必要があります.Put()空き接続を接続プールに戻す
func (p *ConnPool) Put(c Conn) {
p.mu.Lock()
if !p.closed { // ,
p.idle.PushFront(idleConn{c: c, t: time.Now()})
if p.idle.Len() > p.MaxIdle {
oldConn := p.idle.Remove(p.idle.Back()).(idleConn)
oldConn.c.Close() // ,
}
}
if p.cond != nil {
p.cond.Signal()
}
p.mu.Unlock()
}
空き接続をプールに戻すと、
p.cond.Signal()
から信号通知が送信され、待機しているget()関数が空き接続オブジェクトを取得できることを通知するClose()接続プールを閉じる
func (p *ConnPool) Close() {
p.mu.Lock()
idle := p.idle
p.idle.Init()
p.closed = true
p.active -= idle.Len()
if p.cond != nil {
p.cond.Broadcast() // get()
}
p.mu.Unlock()
for e := idle.Front(); e != nil; e = e.Next() {
e.Value.(idleConn).c.Close() //
}
}
ActiveCount()アクティブな接続数を取得
func (p *ConnPool) ActiveCount() int {
p.mu.Lock()
active := p.active
p.mu.Unlock()
return active
}
release()接続オブジェクトの消去
func (p *ConnPool) release() {
p.active -= 1
if p.cond != nil {
p.cond.Signal()
}
}
Activeが減少すると、ここの
p.cond.Signal()
は、ブロックされたget()関数に新しい接続オブジェクトを取得するように通知することができる.プールの接続に使用する関連標準ライブラリ
import (
"container/list"
"errors"
"sync"
"time"
)
以上のgo実装の接続プールは参考にして使用することができて、Connインタフェースのタイプを修正したり交換したりすればいいので、問題があれば、皆さんの伝言を歓迎します.