Redigo:Redis服务器关闭时快速失败

Redigo:Redis服务器关闭时快速失败

问题描述:

当我连接的Redis服务器出现故障时,我正在努力快速失败,希望拥有一个强大的解决方案。Redigo:Redis服务器关闭时快速失败

我使用redigo和我设置了一个连接池,像这样:

// This has other stuff in it in the code, use it as a 
// central repository for things we want in memory 
type State struct{ 
    redisPool *redis.Pool 
} 

func (state *State) GetRedisConn() redis.Conn { 
    return state.redisPool.Get() 
} 

func main() { 
    state.redisPool = &redis.Pool{ 
     MaxIdle:  200, 
     MaxActive: 9000, 
     IdleTimeout: time.Minute, 
     Dial: func() (redis.Conn, error) { 
      return redis.Dial("tcp", *redisAddress, 
       redis.DialConnectTimeout(1*time.Second), 
       redis.DialReadTimeout(100*time.Millisecond), 
       redis.DialWriteTimeout(100*time.Millisecond), 
      ) 
     }, 
    } 
} 

,并要求新的连接,并使用它们,像这样:

t0 := time.Now() 

conn := state.GetRedisConn() 
if conn != nil && conn.Err() == nil { 
    defer conn.Close() 
    // Do stuff 
else { 
    log.Printf("no redis probably") 
} 

log.Println(time.Now().Sub(t0).Seconds()) 

虽然Redis的就到了,这很好,事情发生在毫秒。当我把我的第75百分位降到7秒以上时,我的第99百分位高达10秒(我可以在普罗米修斯看到这一点)

我在做什么错了?为什么这不会更快? 我的印象是,redis.DialConnectTimeout(1*time.Second)会在1秒内限制这个问题,但似乎并非如此。

编辑:事实证明,这是由于我在Prometheus中犯的一个错误,设置桶太大,所以虽然redis在一秒钟后超时,但我的桶已经设置了1s桶和一个10s桶,所以我的请求(刚刚超过1s)结束了10s桶,结果偏差。我相信这个讨论在某些时候会对某些人有用。

+3

你可能充斥系统连接尝试。您需要实施某种断路器,以在服务器关闭时退出。另外9000最大连接数似乎太高(因为redis服务器一次只能处理一个请求),此外,这意味着当服务器停止响应时,可能会很快耗尽临时端口。 – JimB

+2

独立于延迟问题,你应该'conn:= state.GetRedisConn();推迟conn.Close()'。池总是返回连接必须关闭的连接。 –

在发生故障后限速尝试拨号:

func main() { 
    var (
     nextDial time.Time 
     mu sync.Mutex 
    ) 
    state.redisPool = &redis.Pool{ 
     MaxIdle:  200, 
     MaxActive: 9000, 
     IdleTimeout: time.Minute, 
     Dial: func() (redis.Conn, error) { 
      mu.Lock() // Dial can be called concurrently 
      defer mu.Unlock() 
      if time.Now().Before(nextDial) { 
       return nil, errors.New("waiting for dial") 
      } 
      c, err := redis.Dial("tcp", *redisAddress, 
       redis.DialConnectTimeout(1*time.Second), 
       redis.DialReadTimeout(100*time.Millisecond), 
       redis.DialWriteTimeout(100*time.Millisecond), 
      ) 
      if err == nil { 
       nextDial = time.Time{} 
      } else { 
       nextDial = time.Now().Add(time.Second) // don't attempt dial for one second 
      } 
      return c, err 
     }, 
    } 
}