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 ответ
Вы закрываете соединение после отправки по каналам. Если канал блокируется, вы не закрываете соединения, что может привести к появившейся ошибке. так что не откладывайте, закройте соединение явно.
Я не думаю, что это проблема, но хорошая идея - установить тайм-аут на ваши соединения с
DialTimeout
,Убедитесь, что у вас есть правильное
TestOnBorrow
функция, чтобы избавиться от мертвых соединений, особенно если у вас есть тайм-аут. Я обычно делаю PING, если соединение не используется более 3 секунд (функция получает время простоя в качестве параметра)Попробуйте установить
MaxIdle
я помню, что были проблемы с пулом, которые были решены путем увеличения этого параметра в пуле.