redigo: получение набора tcp: подключение: невозможно назначить запрошенный адрес

У меня есть приложение, которое делает около 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#Pool).

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

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

(54.160.xxx.xx:yyyy — хост Redis)

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

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

Есть идеи?

ИЗМЕНИТЬ

Вот мой код инициализации пула (делаю это в 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: проблема решена путем применения материалов, предложенных в ответе ниже!

Новый код для инициализации пула:

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()
    }
}

person orcaman    schedule 15.06.2016    source источник
comment
пожалуйста, приложите код инициализации пула.   -  person Not_a_Golfer    schedule 15.06.2016
comment
@Not_a_Golfer готово   -  person orcaman    schedule 15.06.2016


Ответы (1)


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

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

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

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

person Not_a_Golfer    schedule 15.06.2016
comment
Потрясающий! ты жжешь. Добавлен код, решающий эту проблему для будущих поколений. - person orcaman; 15.06.2016