7

redis (redislabs でホストされている) に対して、1 秒あたり約 400 回の読み取りと 1 秒あたり 100 回の書き込みを行うアプリケーションがあります。アプリケーションはgithub.com/garyburd/redigoパッケージを redis プロキシとして使用しています。

読み取りと書き込みに使用される唯一の関数が2つあります。

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 ホストです)

これが発生し始めたとき、約 600 の接続しかないことを redis で確認しましたが、これはそれほど多くないように思えます。

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

1 に答える 1

9
  1. チャネルで送信した後に接続を閉じています。チャネルがブロックされている場合、接続を閉じていないため、表示されているエラーが発生します。延期するだけでなく、明示的に接続を閉じてください。

  2. それが問題だとは思いませんが、とにかく良い考えです - との接続にタイムアウトを設定してくださいDialTimeout

  3. TestOnBorrow特にタイムアウトがある場合は、死んだ接続を取り除くための適切な機能があることを確認してください。私は通常、接続が 3 秒以上アイドル状態の場合に PING を実行します (関数はアイドル時間をパラメーターとして受け取ります)。

  4. より大きな数に設定MaxIdleしてみてください。プールでそのパラメーターを増やすことで解決されたプールの問題があったことを覚えています。

于 2016-06-15T07:22:34.407 に答える