1

当我连接的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 启动时,这很好用,事情会在几毫秒内发生。当我将 redis 降低到第 75 个百分位时,我的第 75 个百分位上升到 7+ 秒,而我的第 99 个百分位上升到 10 秒(我可以在 prometheus 上看到这个)

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

编辑:事实证明这是由于我在 Prometheus 中犯的一个错误,将存储桶设置得太大,所以当 redis 在一秒钟后超时正常时,我的存储桶已经设置了一个 1s 存储桶和一个 10s 存储桶,所以我的请求(刚刚超过 1 秒)最终进入了 10 秒的存储桶,从而扭曲了结果。我相信这个讨论会在某些时候对某人有用。

4

1 回答 1

2

失败后的速率限制拨号尝试:

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
        },
    }
}
于 2017-07-01T20:55:38.957 回答