mirror of https://github.com/go-redis/redis.git
257 lines
4.1 KiB
Go
257 lines
4.1 KiB
Go
package redis
|
|
|
|
import (
|
|
"container/list"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/vmihailenco/bufio"
|
|
)
|
|
|
|
type pool interface {
|
|
Get() (*conn, bool, error)
|
|
Put(*conn) error
|
|
Remove(*conn) error
|
|
Len() int
|
|
Close() error
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type conn struct {
|
|
Cn net.Conn
|
|
Rd reader
|
|
|
|
UsedAt time.Time
|
|
|
|
readTimeout, writeTimeout time.Duration
|
|
}
|
|
|
|
func newConn(netcn net.Conn) *conn {
|
|
cn := &conn{
|
|
Cn: netcn,
|
|
}
|
|
cn.Rd = bufio.NewReaderSize(cn, 1024)
|
|
return cn
|
|
}
|
|
|
|
func (cn *conn) Read(b []byte) (int, error) {
|
|
if cn.readTimeout != 0 {
|
|
cn.Cn.SetReadDeadline(time.Now().Add(cn.readTimeout))
|
|
} else {
|
|
cn.Cn.SetReadDeadline(time.Time{})
|
|
}
|
|
return cn.Cn.Read(b)
|
|
}
|
|
|
|
func (cn *conn) Write(b []byte) (int, error) {
|
|
if cn.writeTimeout != 0 {
|
|
cn.Cn.SetWriteDeadline(time.Now().Add(cn.writeTimeout))
|
|
} else {
|
|
cn.Cn.SetReadDeadline(time.Time{})
|
|
}
|
|
return cn.Cn.Write(b)
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type connPool struct {
|
|
dial func() (net.Conn, error)
|
|
|
|
cond *sync.Cond
|
|
conns *list.List
|
|
|
|
readTimeout, writeTimeout time.Duration
|
|
|
|
size, maxSize int
|
|
idleTimeout time.Duration
|
|
}
|
|
|
|
func newConnPool(
|
|
dial func() (net.Conn, error),
|
|
maxSize int,
|
|
idleTimeout time.Duration,
|
|
) *connPool {
|
|
return &connPool{
|
|
dial: dial,
|
|
|
|
cond: sync.NewCond(&sync.Mutex{}),
|
|
conns: list.New(),
|
|
|
|
maxSize: maxSize,
|
|
idleTimeout: idleTimeout,
|
|
}
|
|
}
|
|
|
|
func (p *connPool) Get() (*conn, bool, error) {
|
|
defer p.cond.L.Unlock()
|
|
p.cond.L.Lock()
|
|
|
|
for p.conns.Len() == 0 && p.size >= p.maxSize {
|
|
p.cond.Wait()
|
|
}
|
|
|
|
if p.idleTimeout > 0 {
|
|
for e := p.conns.Front(); e != nil; e = e.Next() {
|
|
cn := e.Value.(*conn)
|
|
if time.Since(cn.UsedAt) > p.idleTimeout {
|
|
p.conns.Remove(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
if p.conns.Len() == 0 {
|
|
rw, err := p.dial()
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
p.size++
|
|
return newConn(rw), true, nil
|
|
}
|
|
|
|
elem := p.conns.Front()
|
|
p.conns.Remove(elem)
|
|
return elem.Value.(*conn), false, nil
|
|
}
|
|
|
|
func (p *connPool) Put(cn *conn) error {
|
|
if cn.Rd.Buffered() != 0 {
|
|
panic("pool: put connection with buffered data")
|
|
}
|
|
|
|
p.cond.L.Lock()
|
|
cn.UsedAt = time.Now()
|
|
p.conns.PushFront(cn)
|
|
p.cond.Signal()
|
|
p.cond.L.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (p *connPool) Remove(cn *conn) error {
|
|
var err error
|
|
if cn != nil {
|
|
err = p.closeConn(cn)
|
|
}
|
|
p.cond.L.Lock()
|
|
p.size--
|
|
p.cond.Signal()
|
|
p.cond.L.Unlock()
|
|
return err
|
|
}
|
|
|
|
func (p *connPool) Len() int {
|
|
defer p.cond.L.Unlock()
|
|
p.cond.L.Lock()
|
|
return p.conns.Len()
|
|
}
|
|
|
|
func (p *connPool) Size() int {
|
|
defer p.cond.L.Unlock()
|
|
p.cond.L.Lock()
|
|
return p.size
|
|
}
|
|
|
|
func (p *connPool) Close() error {
|
|
defer p.cond.L.Unlock()
|
|
p.cond.L.Lock()
|
|
|
|
for e := p.conns.Front(); e != nil; e = e.Next() {
|
|
if err := p.closeConn(e.Value.(*conn)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
p.conns.Init()
|
|
p.size = 0
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *connPool) closeConn(cn *conn) error {
|
|
return cn.Cn.Close()
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type singleConnPool struct {
|
|
pool pool
|
|
|
|
l sync.RWMutex
|
|
cn *conn
|
|
reusable bool
|
|
}
|
|
|
|
func newSingleConnPool(pool pool, cn *conn, reusable bool) *singleConnPool {
|
|
return &singleConnPool{
|
|
pool: pool,
|
|
cn: cn,
|
|
reusable: reusable,
|
|
}
|
|
}
|
|
|
|
func (p *singleConnPool) Get() (*conn, bool, error) {
|
|
p.l.RLock()
|
|
if p.cn != nil {
|
|
p.l.RUnlock()
|
|
return p.cn, false, nil
|
|
}
|
|
p.l.RUnlock()
|
|
|
|
defer p.l.Unlock()
|
|
p.l.Lock()
|
|
|
|
cn, isNew, err := p.pool.Get()
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
p.cn = cn
|
|
|
|
return cn, isNew, nil
|
|
}
|
|
|
|
func (p *singleConnPool) Put(cn *conn) error {
|
|
defer p.l.Unlock()
|
|
p.l.Lock()
|
|
if p.cn != cn {
|
|
panic("p.cn != cn")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *singleConnPool) Remove(cn *conn) error {
|
|
defer p.l.Unlock()
|
|
p.l.Lock()
|
|
if p.cn != cn {
|
|
panic("p.cn != cn")
|
|
}
|
|
p.cn = nil
|
|
return nil
|
|
}
|
|
|
|
func (p *singleConnPool) Len() int {
|
|
defer p.l.Unlock()
|
|
p.l.Lock()
|
|
if p.cn == nil {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func (p *singleConnPool) Close() error {
|
|
defer p.l.Unlock()
|
|
p.l.Lock()
|
|
|
|
var err error
|
|
if p.cn != nil {
|
|
if p.reusable {
|
|
err = p.pool.Put(p.cn)
|
|
} else {
|
|
err = p.pool.Remove(p.cn)
|
|
}
|
|
}
|
|
p.cn = nil
|
|
|
|
return err
|
|
}
|