redigo: получение набора tcp: connect: не удается назначить запрошенный адрес

У меня есть приложение, которое делает около 400 операций чтения в секунду и 100 операций записи в секунду для redis (размещенных на redislabs). Приложение использует github.com/garyburd/redigo Пакет как Redis прокси.

У меня есть две функции, которые используются только для чтения и записи:

func getCachedVPAIDConfig(key string) chan *cachedVPAIDConfig {
    c := make(chan *cachedVPAIDConfig)
    go func() {
        p := pool.Get()
        defer p.Close()

        switch p.Err() {
        case nil:
            item, err := redis.Bytes(p.Do("GET", key))
            if err != nil {
                c <- &cachedVPAIDConfig{nil, err}
                return
            }

            c <- &cachedVPAIDConfig{item, nil}
        default:
            c <- &cachedVPAIDConfig{nil, p.Err()}
            return
        }
    }()
    return c
}



func setCachedVPAIDConfig(key string, j []byte) chan error {
    c := make(chan error)
    go func() {
        p := pool.Get()
        defer p.Close()

        switch p.Err() {
        case nil:
            _, err := p.Do("SET", key, j)

            if err != nil {
                c <- err
                return
            }

            c <- nil
        default:
            c <- p.Err()
            return
        }
    }()
    return c
}

Как видите, я использую рекомендованный механизм пула соединений ( http://godoc.org/github.com/garyburd/redigo/redis).

Я вызываю эти функции при каждом запросе http, который получает конечная точка приложения. Проблема в том, что, как только приложение начинает получать запросы, оно сразу же начинает выдавать ошибку

dial tcp 54.160.xxx.xx:yyyy: connect: cannot assign requested address

(54.160.xxx.xx:yyyy - хост redis)

Я вижу, что на Redis есть только около 600 соединений, когда это начинает происходить, что не так уж много.

Я пытался играть с MaxActive установка pool, установив его где-нибудь между 1000 и 50К, но результат тот же.

Есть идеи?

РЕДАКТИРОВАТЬ

Вот мой код инициализации пула (делает это в func init):

pool = redis.Pool{
    MaxActive:   1000, // note: I tried changing this to 50K, result the same
    Dial: func() (redis.Conn, error) {
        c, err := redis.Dial("tcp", redisHost)
        if err != nil {
            return nil, err
        }
        if _, err := c.Do("AUTH", redisPassword); err != nil {
            c.Close()
            return nil, err
        }
        return c, err
    },
}

Изменить 2: проблема решена путем применения материала, предложенного в ответе ниже!

Новый код для пула init:

pool = redis.Pool{
    MaxActive:   500,
    MaxIdle:     500,
    IdleTimeout: 5 * time.Second,
    Dial: func() (redis.Conn, error) {
        c, err := redis.DialTimeout("tcp", redisHost, 100*time.Millisecond, 100*time.Millisecond, 100*time.Millisecond)
        if err != nil {
            return nil, err
        }
        if _, err := c.Do("AUTH", redisPassword); err != nil {
            c.Close()
            return nil, err
        }
        return c, err
    },
}

Этот новый init делает так, что тайм-ауты get и set обрабатываются внутри redigo, поэтому мне больше не нужно возвращать канал в функциях getCachedVPAIDConfig и setCachedVPAIDConfig. Вот как они выглядят сейчас:

func setCachedVPAIDConfig(key string, j []byte) error {
    p := pool.Get()
    switch p.Err() {
    case nil:
        _, err := p.Do("SET", key, j)
        p.Close()
        return err
    default:
        p.Close()
        return p.Err()
    }
}

func getCachedVPAIDConfig(key string) ([]byte, error) {
    p := pool.Get()
    switch p.Err() {
    case nil:
        item, err := redis.Bytes(p.Do("GET", key))
        p.Close()
        return item, err
    default:
        p.Close()
        return nil, p.Err()
    }
}

1 ответ

Решение
  1. Вы закрываете соединение после отправки по каналам. Если канал блокируется, вы не закрываете соединения, что может привести к появившейся ошибке. так что не откладывайте, закройте соединение явно.

  2. Я не думаю, что это проблема, но хорошая идея - установить тайм-аут на ваши соединения с DialTimeout,

  3. Убедитесь, что у вас есть правильное TestOnBorrow функция, чтобы избавиться от мертвых соединений, особенно если у вас есть тайм-аут. Я обычно делаю PING, если соединение не используется более 3 секунд (функция получает время простоя в качестве параметра)

  4. Попробуйте установить MaxIdle я помню, что были проблемы с пулом, которые были решены путем увеличения этого параметра в пуле.

Другие вопросы по тегам