7

我有一个应用程序每秒对 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 个连接,这听起来并不多。

我尝试使用 的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
    },
}

这个新的初始化使得 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()
    }
}
4

1 回答 1

9
  1. 您在通道上发送后关闭连接,如果通道阻塞,您没有关闭连接,这将导致您看到的错误。所以不要只是推迟,明确地关闭连接。

  2. 我认为这不是问题,但无论如何都是一个好主意 - 为您的连接设置超时DialTimeout

  3. 确保你有一个适当的TestOnBorrow函数来摆脱死连接,特别是如果你有超时。如果连接空闲超过 3 秒,我通常会执行 PING(函数接收空闲时间作为参数)

  4. 尝试设置MaxIdle为更大的数字,我记得通过增加池中的参数来解决池化问题。

于 2016-06-15T07:22:34.407 回答