Improve ReceiveMessage.

This commit is contained in:
Vladimir Mihailenco 2015-12-02 15:40:44 +02:00
parent 5efe0cceb6
commit 42141f11d1
4 changed files with 46 additions and 31 deletions

View File

@ -49,7 +49,8 @@ func (c *Client) Multi() *Multi {
func (c *Multi) putConn(cn *conn, ei error) { func (c *Multi) putConn(cn *conn, ei error) {
var err error var err error
if isBadConn(cn, ei) { if isBadConn(cn, ei) {
err = c.base.connPool.Remove(nil) // nil to force removal // Close current connection.
c.base.connPool.(*stickyConnPool).Reset()
} else { } else {
err = c.base.connPool.Put(cn) err = c.base.connPool.Put(cn)
} }

15
pool.go
View File

@ -137,7 +137,7 @@ func newConnPool(opt *Options) *connPool {
p := &connPool{ p := &connPool{
dialer: newConnDialer(opt), dialer: newConnDialer(opt),
rl: ratelimit.New(2*opt.getPoolSize(), time.Second), rl: ratelimit.New(3*opt.getPoolSize(), time.Second),
opt: opt, opt: opt,
conns: newConnList(opt.getPoolSize()), conns: newConnList(opt.getPoolSize()),
freeConns: make(chan *conn, opt.getPoolSize()), freeConns: make(chan *conn, opt.getPoolSize()),
@ -458,11 +458,7 @@ func (p *stickyConnPool) Remove(cn *conn) error {
if cn != nil && p.cn != cn { if cn != nil && p.cn != cn {
panic("p.cn != cn") panic("p.cn != cn")
} }
if cn == nil {
return p.remove()
} else {
return nil return nil
}
} }
func (p *stickyConnPool) Len() int { func (p *stickyConnPool) Len() int {
@ -483,6 +479,15 @@ func (p *stickyConnPool) FreeLen() int {
return 0 return 0
} }
func (p *stickyConnPool) Reset() (err error) {
p.mx.Lock()
if p.cn != nil {
err = p.remove()
}
p.mx.Unlock()
return err
}
func (p *stickyConnPool) Close() error { func (p *stickyConnPool) Close() error {
defer p.mx.Unlock() defer p.mx.Unlock()
p.mx.Lock() p.mx.Lock()

View File

@ -235,7 +235,7 @@ func (c *PubSub) Receive() (interface{}, error) {
func (c *PubSub) reconnect() { func (c *PubSub) reconnect() {
// Close current connection. // Close current connection.
c.connPool.Remove(nil) // nil to force removal c.connPool.(*stickyConnPool).Reset()
if len(c.channels) > 0 { if len(c.channels) > 0 {
if err := c.Subscribe(c.channels...); err != nil { if err := c.Subscribe(c.channels...); err != nil {
@ -252,39 +252,41 @@ func (c *PubSub) reconnect() {
// ReceiveMessage returns a message or error. It automatically // ReceiveMessage returns a message or error. It automatically
// reconnects to Redis in case of network errors. // reconnects to Redis in case of network errors.
func (c *PubSub) ReceiveMessage() (*Message, error) { func (c *PubSub) ReceiveMessage() (*Message, error) {
var badConn bool var errNum int
for { for {
msgi, err := c.ReceiveTimeout(5 * time.Second) msgi, err := c.ReceiveTimeout(5 * time.Second)
if err != nil { if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() { if !isNetworkError(err) {
if badConn {
c.reconnect()
badConn = false
continue
}
err := c.Ping("")
if err != nil {
c.reconnect()
} else {
badConn = true
}
continue
}
if isNetworkError(err) {
c.reconnect()
continue
}
return nil, err return nil, err
} }
goodConn := errNum == 0
errNum++
if goodConn {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
err := c.Ping("")
if err == nil {
continue
}
log.Printf("redis: PubSub.Ping failed: %s", err)
}
}
if errNum > 2 {
time.Sleep(time.Second)
}
c.reconnect()
continue
}
// Reset error number.
errNum = 0
switch msg := msgi.(type) { switch msg := msgi.(type) {
case *Subscription: case *Subscription:
// Ignore. // Ignore.
case *Pong: case *Pong:
badConn = false
// Ignore. // Ignore.
case *Message: case *Message:
return msg, nil return msg, nil

View File

@ -260,9 +260,9 @@ var _ = Describe("PubSub", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer pubsub.Close() defer pubsub.Close()
cn, _, err := pubsub.Pool().Get() cn1, _, err := pubsub.Pool().Get()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
cn.SetNetConn(&badConn{ cn1.SetNetConn(&badConn{
readErr: errTimeout, readErr: errTimeout,
writeErr: errTimeout, writeErr: errTimeout,
}) })
@ -286,7 +286,7 @@ var _ = Describe("PubSub", func() {
wg.Wait() wg.Wait()
}) })
It("should not panic on Close", func() { It("should return on Close", func() {
pubsub, err := client.Subscribe("mychannel") pubsub, err := client.Subscribe("mychannel")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer pubsub.Close() defer pubsub.Close()
@ -297,13 +297,20 @@ var _ = Describe("PubSub", func() {
defer GinkgoRecover() defer GinkgoRecover()
wg.Done() wg.Done()
_, err := pubsub.ReceiveMessage() _, err := pubsub.ReceiveMessage()
Expect(err).To(MatchError("redis: client is closed")) Expect(err).To(MatchError("redis: client is closed"))
wg.Done()
}() }()
wg.Wait() wg.Wait()
wg.Add(1)
err = pubsub.Close() err = pubsub.Close()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
wg.Wait()
}) })
}) })