redigo:获取拨号 tcp:connect:无法分配请求的地址
redigo: getting dial tcp: connect: cannot assign requested address
我有一个应用程序每秒对 redis 进行大约 400 次读取和每秒 100 次写入(托管在 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 个连接,这听起来并不多。
我尝试使用 pool
的 MaxActive
设置,将其设置在 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 内部处理,所以我不再需要 return 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()
}
}
您在通道上发送后关闭连接,如果通道阻塞您没有关闭连接,这将导致您看到的错误。所以不要只是推迟,明确关闭连接。
我认为这不是问题,但无论如何都是个好主意 - 为您与 DialTimeout
.
的连接设置超时
确保你有一个正确的 TestOnBorrow
函数来摆脱死连接,尤其是在你有超时的情况下。如果连接空闲时间超过 3 秒,我通常会执行 PING(该函数接收空闲时间作为参数)
也尝试将 MaxIdle
设置为更大的数字,我记得通过增加池中的参数解决了池化问题。
我有一个应用程序每秒对 redis 进行大约 400 次读取和每秒 100 次写入(托管在 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 个连接,这听起来并不多。
我尝试使用 pool
的 MaxActive
设置,将其设置在 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 内部处理,所以我不再需要 return 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()
}
}
您在通道上发送后关闭连接,如果通道阻塞您没有关闭连接,这将导致您看到的错误。所以不要只是推迟,明确关闭连接。
我认为这不是问题,但无论如何都是个好主意 - 为您与
DialTimeout
. 的连接设置超时
确保你有一个正确的
TestOnBorrow
函数来摆脱死连接,尤其是在你有超时的情况下。如果连接空闲时间超过 3 秒,我通常会执行 PING(该函数接收空闲时间作为参数)也尝试将
MaxIdle
设置为更大的数字,我记得通过增加池中的参数解决了池化问题。